##// 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 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Pdb debugger class.
4 4
5 5 Modified from the standard pdb.Pdb class to avoid including readline, so that
6 6 the command line completion of other programs which include this isn't
7 7 damaged.
8 8
9 9 In the future, this class will be expanded with improvements over the standard
10 10 pdb.
11 11
12 12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
13 13 changes. Licensing should therefore be under the standard Python terms. For
14 14 details on the PSF (Python Software Foundation) standard license, see:
15 15
16 16 http://www.python.org/2.2.3/license.html"""
17 17
18 18 #*****************************************************************************
19 19 #
20 20 # This file is licensed under the PSF license.
21 21 #
22 22 # Copyright (C) 2001 Python Software Foundation, www.python.org
23 23 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
24 24 #
25 25 #
26 26 #*****************************************************************************
27 27 from __future__ import print_function
28 28
29 29 import bdb
30 30 import linecache
31 31 import sys
32 32
33 33 from IPython.utils import PyColorize, ulinecache
34 34 from IPython.core import ipapi
35 35 from IPython.utils import coloransi, io, openpy, py3compat
36 36 from IPython.core.excolors import exception_colors
37 37
38 38 # See if we can use pydb.
39 39 has_pydb = False
40 40 prompt = 'ipdb> '
41 41 #We have to check this directly from sys.argv, config struct not yet available
42 42 if '--pydb' in sys.argv:
43 43 try:
44 44 import pydb
45 45 if hasattr(pydb.pydb, "runl") and pydb.version>'1.17':
46 46 # Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we
47 47 # better protect against it.
48 48 has_pydb = True
49 49 except ImportError:
50 50 print("Pydb (http://bashdb.sourceforge.net/pydb/) does not seem to be available")
51 51
52 52 if has_pydb:
53 53 from pydb import Pdb as OldPdb
54 54 #print "Using pydb for %run -d and post-mortem" #dbg
55 55 prompt = 'ipydb> '
56 56 else:
57 57 from pdb import Pdb as OldPdb
58 58
59 59 # Allow the set_trace code to operate outside of an ipython instance, even if
60 60 # it does so with some limitations. The rest of this support is implemented in
61 61 # the Tracer constructor.
62 62 def BdbQuit_excepthook(et,ev,tb):
63 63 if et==bdb.BdbQuit:
64 64 print('Exiting Debugger.')
65 65 else:
66 66 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
67 67
68 68 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
69 69 print('Exiting Debugger.')
70 70
71 71
72 72 class Tracer(object):
73 73 """Class for local debugging, similar to pdb.set_trace.
74 74
75 75 Instances of this class, when called, behave like pdb.set_trace, but
76 76 providing IPython's enhanced capabilities.
77 77
78 78 This is implemented as a class which must be initialized in your own code
79 79 and not as a standalone function because we need to detect at runtime
80 80 whether IPython is already active or not. That detection is done in the
81 81 constructor, ensuring that this code plays nicely with a running IPython,
82 82 while functioning acceptably (though with limitations) if outside of it.
83 83 """
84 84
85 85 def __init__(self,colors=None):
86 86 """Create a local debugger instance.
87 87
88 88 :Parameters:
89 89
90 90 - `colors` (None): a string containing the name of the color scheme to
91 91 use, it must be one of IPython's valid color schemes. If not given, the
92 92 function will default to the current IPython scheme when running inside
93 93 IPython, and to 'NoColor' otherwise.
94 94
95 95 Usage example:
96 96
97 97 from IPython.core.debugger import Tracer; debug_here = Tracer()
98 98
99 99 ... later in your code
100 100 debug_here() # -> will open up the debugger at that point.
101 101
102 102 Once the debugger activates, you can use all of its regular commands to
103 103 step through code, set breakpoints, etc. See the pdb documentation
104 104 from the Python standard library for usage details.
105 105 """
106 106
107 107 try:
108 108 ip = get_ipython()
109 109 except NameError:
110 110 # Outside of ipython, we set our own exception hook manually
111 111 BdbQuit_excepthook.excepthook_ori = sys.excepthook
112 112 sys.excepthook = BdbQuit_excepthook
113 113 def_colors = 'NoColor'
114 114 try:
115 115 # Limited tab completion support
116 116 import readline
117 117 readline.parse_and_bind('tab: complete')
118 118 except ImportError:
119 119 pass
120 120 else:
121 121 # In ipython, we use its custom exception handler mechanism
122 122 def_colors = ip.colors
123 123 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
124 124
125 125 if colors is None:
126 126 colors = def_colors
127 127
128 128 # The stdlib debugger internally uses a modified repr from the `repr`
129 129 # module, that limits the length of printed strings to a hardcoded
130 130 # limit of 30 characters. That much trimming is too aggressive, let's
131 131 # at least raise that limit to 80 chars, which should be enough for
132 132 # most interactive uses.
133 133 try:
134 134 from repr import aRepr
135 135 aRepr.maxstring = 80
136 136 except:
137 137 # This is only a user-facing convenience, so any error we encounter
138 138 # here can be warned about but can be otherwise ignored. These
139 139 # printouts will tell us about problems if this API changes
140 140 import traceback
141 141 traceback.print_exc()
142 142
143 143 self.debugger = Pdb(colors)
144 144
145 145 def __call__(self):
146 146 """Starts an interactive debugger at the point where called.
147 147
148 148 This is similar to the pdb.set_trace() function from the std lib, but
149 149 using IPython's enhanced debugger."""
150 150
151 151 self.debugger.set_trace(sys._getframe().f_back)
152 152
153 153
154 154 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
155 155 """Make new_fn have old_fn's doc string. This is particularly useful
156 156 for the do_... commands that hook into the help system.
157 157 Adapted from from a comp.lang.python posting
158 158 by Duncan Booth."""
159 159 def wrapper(*args, **kw):
160 160 return new_fn(*args, **kw)
161 161 if old_fn.__doc__:
162 162 wrapper.__doc__ = old_fn.__doc__ + additional_text
163 163 return wrapper
164 164
165 165
166 166 def _file_lines(fname):
167 167 """Return the contents of a named file as a list of lines.
168 168
169 169 This function never raises an IOError exception: if the file can't be
170 170 read, it simply returns an empty list."""
171 171
172 172 try:
173 173 outfile = open(fname)
174 174 except IOError:
175 175 return []
176 176 else:
177 177 out = outfile.readlines()
178 178 outfile.close()
179 179 return out
180 180
181 181
182 182 class Pdb(OldPdb):
183 183 """Modified Pdb class, does not load readline."""
184 184
185 185 def __init__(self,color_scheme='NoColor',completekey=None,
186 186 stdin=None, stdout=None):
187 187
188 188 # Parent constructor:
189 189 if has_pydb and completekey is None:
190 190 OldPdb.__init__(self,stdin=stdin,stdout=io.stdout)
191 191 else:
192 192 OldPdb.__init__(self,completekey,stdin,stdout)
193 193
194 194 self.prompt = prompt # The default prompt is '(Pdb)'
195 195
196 196 # IPython changes...
197 197 self.is_pydb = has_pydb
198 198
199 199 self.shell = ipapi.get()
200 200
201 201 if self.is_pydb:
202 202
203 203 # interactiveshell.py's ipalias seems to want pdb's checkline
204 204 # which located in pydb.fn
205 205 import pydb.fns
206 206 self.checkline = lambda filename, lineno: \
207 207 pydb.fns.checkline(self, filename, lineno)
208 208
209 209 self.curframe = None
210 210 self.do_restart = self.new_do_restart
211 211
212 212 self.old_all_completions = self.shell.Completer.all_completions
213 213 self.shell.Completer.all_completions=self.all_completions
214 214
215 215 self.do_list = decorate_fn_with_doc(self.list_command_pydb,
216 216 OldPdb.do_list)
217 217 self.do_l = self.do_list
218 218 self.do_frame = decorate_fn_with_doc(self.new_do_frame,
219 219 OldPdb.do_frame)
220 220
221 221 self.aliases = {}
222 222
223 223 # Create color table: we copy the default one from the traceback
224 224 # module and add a few attributes needed for debugging
225 225 self.color_scheme_table = exception_colors()
226 226
227 227 # shorthands
228 228 C = coloransi.TermColors
229 229 cst = self.color_scheme_table
230 230
231 231 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
232 232 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
233 233
234 234 cst['Linux'].colors.breakpoint_enabled = C.LightRed
235 235 cst['Linux'].colors.breakpoint_disabled = C.Red
236 236
237 237 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
238 238 cst['LightBG'].colors.breakpoint_disabled = C.Red
239 239
240 240 self.set_colors(color_scheme)
241 241
242 242 # Add a python parser so we can syntax highlight source while
243 243 # debugging.
244 244 self.parser = PyColorize.Parser()
245 245
246 246 def set_colors(self, scheme):
247 247 """Shorthand access to the color table scheme selector method."""
248 248 self.color_scheme_table.set_active_scheme(scheme)
249 249
250 250 def interaction(self, frame, traceback):
251 251 self.shell.set_completer_frame(frame)
252 252 OldPdb.interaction(self, frame, traceback)
253 253
254 254 def new_do_up(self, arg):
255 255 OldPdb.do_up(self, arg)
256 256 self.shell.set_completer_frame(self.curframe)
257 257 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
258 258
259 259 def new_do_down(self, arg):
260 260 OldPdb.do_down(self, arg)
261 261 self.shell.set_completer_frame(self.curframe)
262 262
263 263 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
264 264
265 265 def new_do_frame(self, arg):
266 266 OldPdb.do_frame(self, arg)
267 267 self.shell.set_completer_frame(self.curframe)
268 268
269 269 def new_do_quit(self, arg):
270 270
271 271 if hasattr(self, 'old_all_completions'):
272 272 self.shell.Completer.all_completions=self.old_all_completions
273 273
274 274
275 275 return OldPdb.do_quit(self, arg)
276 276
277 277 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
278 278
279 279 def new_do_restart(self, arg):
280 280 """Restart command. In the context of ipython this is exactly the same
281 281 thing as 'quit'."""
282 282 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
283 283 return self.do_quit(arg)
284 284
285 285 def postloop(self):
286 286 self.shell.set_completer_frame(None)
287 287
288 288 def print_stack_trace(self):
289 289 try:
290 290 for frame_lineno in self.stack:
291 291 self.print_stack_entry(frame_lineno, context = 5)
292 292 except KeyboardInterrupt:
293 293 pass
294 294
295 295 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
296 296 context = 3):
297 297 #frame, lineno = frame_lineno
298 298 print(self.format_stack_entry(frame_lineno, '', context), file=io.stdout)
299 299
300 300 # vds: >>
301 301 frame, lineno = frame_lineno
302 302 filename = frame.f_code.co_filename
303 303 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
304 304 # vds: <<
305 305
306 306 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
307 307 import repr
308 308
309 309 ret = []
310 310
311 311 Colors = self.color_scheme_table.active_colors
312 312 ColorsNormal = Colors.Normal
313 313 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
314 314 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
315 315 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
316 316 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
317 317 ColorsNormal)
318 318
319 319 frame, lineno = frame_lineno
320 320
321 321 return_value = ''
322 322 if '__return__' in frame.f_locals:
323 323 rv = frame.f_locals['__return__']
324 324 #return_value += '->'
325 325 return_value += repr.repr(rv) + '\n'
326 326 ret.append(return_value)
327 327
328 328 #s = filename + '(' + `lineno` + ')'
329 329 filename = self.canonic(frame.f_code.co_filename)
330 330 link = tpl_link % py3compat.cast_unicode(filename)
331 331
332 332 if frame.f_code.co_name:
333 333 func = frame.f_code.co_name
334 334 else:
335 335 func = "<lambda>"
336 336
337 337 call = ''
338 338 if func != '?':
339 339 if '__args__' in frame.f_locals:
340 340 args = repr.repr(frame.f_locals['__args__'])
341 341 else:
342 342 args = '()'
343 343 call = tpl_call % (func, args)
344 344
345 345 # The level info should be generated in the same format pdb uses, to
346 346 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
347 347 if frame is self.curframe:
348 348 ret.append('> ')
349 349 else:
350 350 ret.append(' ')
351 351 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
352 352
353 353 start = lineno - 1 - context//2
354 354 lines = ulinecache.getlines(filename)
355 355 start = max(start, 0)
356 356 start = min(start, len(lines) - context)
357 357 lines = lines[start : start + context]
358 358
359 359 for i,line in enumerate(lines):
360 360 show_arrow = (start + 1 + i == lineno)
361 361 linetpl = (frame is self.curframe or show_arrow) \
362 362 and tpl_line_em \
363 363 or tpl_line
364 364 ret.append(self.__format_line(linetpl, filename,
365 365 start + 1 + i, line,
366 366 arrow = show_arrow) )
367 367 return ''.join(ret)
368 368
369 369 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
370 370 bp_mark = ""
371 371 bp_mark_color = ""
372 372
373 373 scheme = self.color_scheme_table.active_scheme_name
374 374 new_line, err = self.parser.format2(line, 'str', scheme)
375 375 if not err: line = new_line
376 376
377 377 bp = None
378 378 if lineno in self.get_file_breaks(filename):
379 379 bps = self.get_breaks(filename, lineno)
380 380 bp = bps[-1]
381 381
382 382 if bp:
383 383 Colors = self.color_scheme_table.active_colors
384 384 bp_mark = str(bp.number)
385 385 bp_mark_color = Colors.breakpoint_enabled
386 386 if not bp.enabled:
387 387 bp_mark_color = Colors.breakpoint_disabled
388 388
389 389 numbers_width = 7
390 390 if arrow:
391 391 # This is the line with the error
392 392 pad = numbers_width - len(str(lineno)) - len(bp_mark)
393 393 if pad >= 3:
394 394 marker = '-'*(pad-3) + '-> '
395 395 elif pad == 2:
396 396 marker = '> '
397 397 elif pad == 1:
398 398 marker = '>'
399 399 else:
400 400 marker = ''
401 401 num = '%s%s' % (marker, str(lineno))
402 402 line = tpl_line % (bp_mark_color + bp_mark, num, line)
403 403 else:
404 404 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
405 405 line = tpl_line % (bp_mark_color + bp_mark, num, line)
406 406
407 407 return line
408 408
409 409 def list_command_pydb(self, arg):
410 410 """List command to use if we have a newer pydb installed"""
411 411 filename, first, last = OldPdb.parse_list_cmd(self, arg)
412 412 if filename is not None:
413 413 self.print_list_lines(filename, first, last)
414 414
415 415 def print_list_lines(self, filename, first, last):
416 416 """The printing (as opposed to the parsing part of a 'list'
417 417 command."""
418 418 try:
419 419 Colors = self.color_scheme_table.active_colors
420 420 ColorsNormal = Colors.Normal
421 421 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
422 422 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
423 423 src = []
424 424 if filename == "<string>" and hasattr(self, "_exec_filename"):
425 425 filename = self._exec_filename
426 426
427 427 for lineno in range(first, last+1):
428 428 line = ulinecache.getline(filename, lineno)
429 429 if not line:
430 430 break
431 431
432 432 if lineno == self.curframe.f_lineno:
433 433 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
434 434 else:
435 435 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
436 436
437 437 src.append(line)
438 438 self.lineno = lineno
439 439
440 440 print(''.join(src), file=io.stdout)
441 441
442 442 except KeyboardInterrupt:
443 443 pass
444 444
445 445 def do_list(self, arg):
446 446 self.lastcmd = 'list'
447 447 last = None
448 448 if arg:
449 449 try:
450 450 x = eval(arg, {}, {})
451 451 if type(x) == type(()):
452 452 first, last = x
453 453 first = int(first)
454 454 last = int(last)
455 455 if last < first:
456 456 # Assume it's a count
457 457 last = first + last
458 458 else:
459 459 first = max(1, int(x) - 5)
460 460 except:
461 461 print('*** Error in argument:', repr(arg))
462 462 return
463 463 elif self.lineno is None:
464 464 first = max(1, self.curframe.f_lineno - 5)
465 465 else:
466 466 first = self.lineno + 1
467 467 if last is None:
468 468 last = first + 10
469 469 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
470 470
471 471 # vds: >>
472 472 lineno = first
473 473 filename = self.curframe.f_code.co_filename
474 474 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
475 475 # vds: <<
476 476
477 477 do_l = do_list
478 478
479 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 483 namespaces = [('Locals', self.curframe.f_locals),
482 484 ('Globals', self.curframe.f_globals)]
483 485 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
484 486
485 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 491 namespaces = [('Locals', self.curframe.f_locals),
488 492 ('Globals', self.curframe.f_globals)]
489 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 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 522 namespaces = [('Locals', self.curframe.f_locals),
494 523 ('Globals', self.curframe.f_globals)]
495 self.shell.find_line_magic('pinfo')("pinfo %s" % arg,
496 namespaces=namespaces)
524 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
497 525
498 526 def checkline(self, filename, lineno):
499 527 """Check whether specified line seems to be executable.
500 528
501 529 Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank
502 530 line or EOF). Warning: testing is not comprehensive.
503 531 """
504 532 #######################################################################
505 533 # XXX Hack! Use python-2.5 compatible code for this call, because with
506 534 # all of our changes, we've drifted from the pdb api in 2.6. For now,
507 535 # changing:
508 536 #
509 537 #line = linecache.getline(filename, lineno, self.curframe.f_globals)
510 538 # to:
511 539 #
512 540 line = linecache.getline(filename, lineno)
513 541 #
514 542 # does the trick. But in reality, we need to fix this by reconciling
515 543 # our updates with the new Pdb APIs in Python 2.6.
516 544 #
517 545 # End hack. The rest of this method is copied verbatim from 2.6 pdb.py
518 546 #######################################################################
519 547
520 548 if not line:
521 549 print('End of file', file=self.stdout)
522 550 return 0
523 551 line = line.strip()
524 552 # Don't allow setting breakpoint at a blank line
525 553 if (not line or (line[0] == '#') or
526 554 (line[:3] == '"""') or line[:3] == "'''"):
527 555 print('*** Blank or comment', file=self.stdout)
528 556 return 0
529 557 return lineno
@@ -1,157 +1,184 b''
1 1 # encoding: utf-8
2 2 """A class for managing IPython extensions.
3 3
4 4 Authors:
5 5
6 6 * Brian Granger
7 7 """
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (C) 2010-2011 The IPython Development Team
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Imports
18 18 #-----------------------------------------------------------------------------
19 19
20 20 import os
21 21 from shutil import copyfile
22 22 import sys
23 23 from urllib import urlretrieve
24 24 from urlparse import urlparse
25 25
26 from IPython.core.error import UsageError
26 27 from IPython.config.configurable import Configurable
27 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 34 # Main class
31 35 #-----------------------------------------------------------------------------
32 36
33 37 class ExtensionManager(Configurable):
34 38 """A class to manage IPython extensions.
35 39
36 40 An IPython extension is an importable Python module that has
37 41 a function with the signature::
38 42
39 43 def load_ipython_extension(ipython):
40 44 # Do things with ipython
41 45
42 46 This function is called after your extension is imported and the
43 47 currently active :class:`InteractiveShell` instance is passed as
44 48 the only argument. You can do anything you want with IPython at
45 49 that point, including defining new magic and aliases, adding new
46 50 components, etc.
47
48 The :func:`load_ipython_extension` will be called again is you
49 load or reload the extension again. It is up to the extension
50 author to add code to manage that.
51
52 You can also optionaly define an :func:`unload_ipython_extension(ipython)`
53 function, which will be called if the user unloads or reloads the extension.
54 The extension manager will only call :func:`load_ipython_extension` again
55 if the extension is reloaded.
51 56
52 57 You can put your extension modules anywhere you want, as long as
53 58 they can be imported by Python's standard import mechanism. However,
54 59 to make it easy to write extensions, you can also put your extensions
55 60 in ``os.path.join(self.ipython_dir, 'extensions')``. This directory
56 61 is added to ``sys.path`` automatically.
57 62 """
58 63
59 64 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
60 65
61 66 def __init__(self, shell=None, config=None):
62 67 super(ExtensionManager, self).__init__(shell=shell, config=config)
63 68 self.shell.on_trait_change(
64 69 self._on_ipython_dir_changed, 'ipython_dir'
65 70 )
71 self.loaded = set()
66 72
67 73 def __del__(self):
68 74 self.shell.on_trait_change(
69 75 self._on_ipython_dir_changed, 'ipython_dir', remove=True
70 76 )
71 77
72 78 @property
73 79 def ipython_extension_dir(self):
74 80 return os.path.join(self.shell.ipython_dir, u'extensions')
75 81
76 82 def _on_ipython_dir_changed(self):
77 83 if not os.path.isdir(self.ipython_extension_dir):
78 84 os.makedirs(self.ipython_extension_dir, mode = 0o777)
79 85
80 86 def load_extension(self, module_str):
81 87 """Load an IPython extension by its module name.
82 88
83 If :func:`load_ipython_extension` returns anything, this function
84 will return that object.
89 Returns the string "already loaded" if the extension is already loaded,
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 96 from IPython.utils.syspathcontext import prepended_to_syspath
87 97
88 98 if module_str not in sys.modules:
89 99 with prepended_to_syspath(self.ipython_extension_dir):
90 100 __import__(module_str)
91 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 107 def unload_extension(self, module_str):
95 108 """Unload an IPython extension by its module name.
96 109
97 110 This function looks up the extension's name in ``sys.modules`` and
98 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 120 if module_str in sys.modules:
101 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 127 def reload_extension(self, module_str):
105 128 """Reload an IPython extension by calling reload.
106 129
107 130 If the module has not been loaded before,
108 131 :meth:`InteractiveShell.load_extension` is called. Otherwise
109 132 :func:`reload` is called and then the :func:`load_ipython_extension`
110 133 function of the module, if it exists is called.
111 134 """
112 135 from IPython.utils.syspathcontext import prepended_to_syspath
113 136
114 with prepended_to_syspath(self.ipython_extension_dir):
115 if module_str in sys.modules:
116 mod = sys.modules[module_str]
137 if (module_str in self.loaded) and (module_str in sys.modules):
138 self.unload_extension(module_str)
139 mod = sys.modules[module_str]
140 with prepended_to_syspath(self.ipython_extension_dir):
117 141 reload(mod)
118 self._call_load_ipython_extension(mod)
119 else:
120 self.load_extension(module_str)
142 if self._call_load_ipython_extension(mod):
143 self.loaded.add(module_str)
144 else:
145 self.load_extension(module_str)
121 146
122 147 def _call_load_ipython_extension(self, mod):
123 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 152 def _call_unload_ipython_extension(self, mod):
127 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 157 def install_extension(self, url, filename=None):
131 158 """Download and install an IPython extension.
132 159
133 160 If filename is given, the file will be so named (inside the extension
134 161 directory). Otherwise, the name from the URL will be used. The file must
135 162 have a .py or .zip extension; otherwise, a ValueError will be raised.
136 163
137 164 Returns the full path to the installed file.
138 165 """
139 166 # Ensure the extension directory exists
140 167 if not os.path.isdir(self.ipython_extension_dir):
141 168 os.makedirs(self.ipython_extension_dir, mode = 0o777)
142 169
143 170 if os.path.isfile(url):
144 171 src_filename = os.path.basename(url)
145 172 copy = copyfile
146 173 else:
147 174 src_filename = urlparse(url).path.split('/')[-1]
148 175 copy = urlretrieve
149 176
150 177 if filename is None:
151 178 filename = src_filename
152 179 if os.path.splitext(filename)[1] not in ('.py', '.zip'):
153 180 raise ValueError("The file must have a .py or .zip extension", filename)
154 181
155 182 filename = os.path.join(self.ipython_extension_dir, filename)
156 183 copy(url, filename)
157 184 return filename
@@ -1,3006 +1,3006 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Main IPython class."""
3 3
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de>
6 6 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
7 7 # Copyright (C) 2008-2011 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 from __future__ import with_statement
18 18 from __future__ import absolute_import
19 19 from __future__ import print_function
20 20
21 21 import __builtin__ as builtin_mod
22 22 import __future__
23 23 import abc
24 24 import ast
25 25 import atexit
26 26 import os
27 27 import re
28 28 import runpy
29 29 import sys
30 30 import tempfile
31 31 import types
32 32 import urllib
33 33 from io import open as io_open
34 34
35 35 from IPython.config.configurable import SingletonConfigurable
36 36 from IPython.core import debugger, oinspect
37 37 from IPython.core import magic
38 38 from IPython.core import page
39 39 from IPython.core import prefilter
40 40 from IPython.core import shadowns
41 41 from IPython.core import ultratb
42 42 from IPython.core.alias import AliasManager, AliasError
43 43 from IPython.core.autocall import ExitAutocall
44 44 from IPython.core.builtin_trap import BuiltinTrap
45 45 from IPython.core.compilerop import CachingCompiler
46 46 from IPython.core.display_trap import DisplayTrap
47 47 from IPython.core.displayhook import DisplayHook
48 48 from IPython.core.displaypub import DisplayPublisher
49 49 from IPython.core.error import UsageError
50 50 from IPython.core.extensions import ExtensionManager
51 51 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
52 52 from IPython.core.formatters import DisplayFormatter
53 53 from IPython.core.history import HistoryManager
54 54 from IPython.core.inputsplitter import IPythonInputSplitter, ESC_MAGIC, ESC_MAGIC2
55 55 from IPython.core.logger import Logger
56 56 from IPython.core.macro import Macro
57 57 from IPython.core.payload import PayloadManager
58 58 from IPython.core.prefilter import PrefilterManager
59 59 from IPython.core.profiledir import ProfileDir
60 60 from IPython.core.pylabtools import pylab_activate
61 61 from IPython.core.prompts import PromptManager
62 62 from IPython.lib.latextools import LaTeXTool
63 63 from IPython.utils import PyColorize
64 64 from IPython.utils import io
65 65 from IPython.utils import py3compat
66 66 from IPython.utils import openpy
67 67 from IPython.utils.doctestreload import doctest_reload
68 68 from IPython.utils.io import ask_yes_no
69 69 from IPython.utils.ipstruct import Struct
70 70 from IPython.utils.path import get_home_dir, get_ipython_dir, get_py_filename, unquote_filename
71 71 from IPython.utils.pickleshare import PickleShareDB
72 72 from IPython.utils.process import system, getoutput
73 73 from IPython.utils.strdispatch import StrDispatch
74 74 from IPython.utils.syspathcontext import prepended_to_syspath
75 75 from IPython.utils.text import (format_screen, LSString, SList,
76 76 DollarFormatter)
77 77 from IPython.utils.traitlets import (Integer, CBool, CaselessStrEnum, Enum,
78 78 List, Unicode, Instance, Type)
79 79 from IPython.utils.warn import warn, error
80 80 import IPython.core.hooks
81 81
82 82 #-----------------------------------------------------------------------------
83 83 # Globals
84 84 #-----------------------------------------------------------------------------
85 85
86 86 # compiled regexps for autoindent management
87 87 dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass')
88 88
89 89 #-----------------------------------------------------------------------------
90 90 # Utilities
91 91 #-----------------------------------------------------------------------------
92 92
93 93 def softspace(file, newvalue):
94 94 """Copied from code.py, to remove the dependency"""
95 95
96 96 oldvalue = 0
97 97 try:
98 98 oldvalue = file.softspace
99 99 except AttributeError:
100 100 pass
101 101 try:
102 102 file.softspace = newvalue
103 103 except (AttributeError, TypeError):
104 104 # "attribute-less object" or "read-only attributes"
105 105 pass
106 106 return oldvalue
107 107
108 108
109 109 def no_op(*a, **kw): pass
110 110
111 111 class NoOpContext(object):
112 112 def __enter__(self): pass
113 113 def __exit__(self, type, value, traceback): pass
114 114 no_op_context = NoOpContext()
115 115
116 116 class SpaceInInput(Exception): pass
117 117
118 118 class Bunch: pass
119 119
120 120
121 121 def get_default_colors():
122 122 if sys.platform=='darwin':
123 123 return "LightBG"
124 124 elif os.name=='nt':
125 125 return 'Linux'
126 126 else:
127 127 return 'Linux'
128 128
129 129
130 130 class SeparateUnicode(Unicode):
131 131 """A Unicode subclass to validate separate_in, separate_out, etc.
132 132
133 133 This is a Unicode based trait that converts '0'->'' and '\\n'->'\n'.
134 134 """
135 135
136 136 def validate(self, obj, value):
137 137 if value == '0': value = ''
138 138 value = value.replace('\\n','\n')
139 139 return super(SeparateUnicode, self).validate(obj, value)
140 140
141 141
142 142 class ReadlineNoRecord(object):
143 143 """Context manager to execute some code, then reload readline history
144 144 so that interactive input to the code doesn't appear when pressing up."""
145 145 def __init__(self, shell):
146 146 self.shell = shell
147 147 self._nested_level = 0
148 148
149 149 def __enter__(self):
150 150 if self._nested_level == 0:
151 151 try:
152 152 self.orig_length = self.current_length()
153 153 self.readline_tail = self.get_readline_tail()
154 154 except (AttributeError, IndexError): # Can fail with pyreadline
155 155 self.orig_length, self.readline_tail = 999999, []
156 156 self._nested_level += 1
157 157
158 158 def __exit__(self, type, value, traceback):
159 159 self._nested_level -= 1
160 160 if self._nested_level == 0:
161 161 # Try clipping the end if it's got longer
162 162 try:
163 163 e = self.current_length() - self.orig_length
164 164 if e > 0:
165 165 for _ in range(e):
166 166 self.shell.readline.remove_history_item(self.orig_length)
167 167
168 168 # If it still doesn't match, just reload readline history.
169 169 if self.current_length() != self.orig_length \
170 170 or self.get_readline_tail() != self.readline_tail:
171 171 self.shell.refill_readline_hist()
172 172 except (AttributeError, IndexError):
173 173 pass
174 174 # Returning False will cause exceptions to propagate
175 175 return False
176 176
177 177 def current_length(self):
178 178 return self.shell.readline.get_current_history_length()
179 179
180 180 def get_readline_tail(self, n=10):
181 181 """Get the last n items in readline history."""
182 182 end = self.shell.readline.get_current_history_length() + 1
183 183 start = max(end-n, 1)
184 184 ghi = self.shell.readline.get_history_item
185 185 return [ghi(x) for x in range(start, end)]
186 186
187 187 #-----------------------------------------------------------------------------
188 188 # Main IPython class
189 189 #-----------------------------------------------------------------------------
190 190
191 191 class InteractiveShell(SingletonConfigurable):
192 192 """An enhanced, interactive shell for Python."""
193 193
194 194 _instance = None
195 195
196 196 autocall = Enum((0,1,2), default_value=0, config=True, help=
197 197 """
198 198 Make IPython automatically call any callable object even if you didn't
199 199 type explicit parentheses. For example, 'str 43' becomes 'str(43)'
200 200 automatically. The value can be '0' to disable the feature, '1' for
201 201 'smart' autocall, where it is not applied if there are no more
202 202 arguments on the line, and '2' for 'full' autocall, where all callable
203 203 objects are automatically called (even if no arguments are present).
204 204 """
205 205 )
206 206 # TODO: remove all autoindent logic and put into frontends.
207 207 # We can't do this yet because even runlines uses the autoindent.
208 208 autoindent = CBool(True, config=True, help=
209 209 """
210 210 Autoindent IPython code entered interactively.
211 211 """
212 212 )
213 213 automagic = CBool(True, config=True, help=
214 214 """
215 215 Enable magic commands to be called without the leading %.
216 216 """
217 217 )
218 218 cache_size = Integer(1000, config=True, help=
219 219 """
220 220 Set the size of the output cache. The default is 1000, you can
221 221 change it permanently in your config file. Setting it to 0 completely
222 222 disables the caching system, and the minimum value accepted is 20 (if
223 223 you provide a value less than 20, it is reset to 0 and a warning is
224 224 issued). This limit is defined because otherwise you'll spend more
225 225 time re-flushing a too small cache than working
226 226 """
227 227 )
228 228 color_info = CBool(True, config=True, help=
229 229 """
230 230 Use colors for displaying information about objects. Because this
231 231 information is passed through a pager (like 'less'), and some pagers
232 232 get confused with color codes, this capability can be turned off.
233 233 """
234 234 )
235 235 colors = CaselessStrEnum(('NoColor','LightBG','Linux'),
236 236 default_value=get_default_colors(), config=True,
237 237 help="Set the color scheme (NoColor, Linux, or LightBG)."
238 238 )
239 239 colors_force = CBool(False, help=
240 240 """
241 241 Force use of ANSI color codes, regardless of OS and readline
242 242 availability.
243 243 """
244 244 # FIXME: This is essentially a hack to allow ZMQShell to show colors
245 245 # without readline on Win32. When the ZMQ formatting system is
246 246 # refactored, this should be removed.
247 247 )
248 248 debug = CBool(False, config=True)
249 249 deep_reload = CBool(False, config=True, help=
250 250 """
251 251 Enable deep (recursive) reloading by default. IPython can use the
252 252 deep_reload module which reloads changes in modules recursively (it
253 253 replaces the reload() function, so you don't need to change anything to
254 254 use it). deep_reload() forces a full reload of modules whose code may
255 255 have changed, which the default reload() function does not. When
256 256 deep_reload is off, IPython will use the normal reload(), but
257 257 deep_reload will still be available as dreload().
258 258 """
259 259 )
260 260 disable_failing_post_execute = CBool(False, config=True,
261 261 help="Don't call post-execute functions that have failed in the past."
262 262 )
263 263 display_formatter = Instance(DisplayFormatter)
264 264 displayhook_class = Type(DisplayHook)
265 265 display_pub_class = Type(DisplayPublisher)
266 266 data_pub_class = None
267 267
268 268 exit_now = CBool(False)
269 269 exiter = Instance(ExitAutocall)
270 270 def _exiter_default(self):
271 271 return ExitAutocall(self)
272 272 # Monotonically increasing execution counter
273 273 execution_count = Integer(1)
274 274 filename = Unicode("<ipython console>")
275 275 ipython_dir= Unicode('', config=True) # Set to get_ipython_dir() in __init__
276 276
277 277 # Input splitter, to split entire cells of input into either individual
278 278 # interactive statements or whole blocks.
279 279 input_splitter = Instance('IPython.core.inputsplitter.IPythonInputSplitter',
280 280 (), {})
281 281 logstart = CBool(False, config=True, help=
282 282 """
283 283 Start logging to the default log file.
284 284 """
285 285 )
286 286 logfile = Unicode('', config=True, help=
287 287 """
288 288 The name of the logfile to use.
289 289 """
290 290 )
291 291 logappend = Unicode('', config=True, help=
292 292 """
293 293 Start logging to the given file in append mode.
294 294 """
295 295 )
296 296 object_info_string_level = Enum((0,1,2), default_value=0,
297 297 config=True)
298 298 pdb = CBool(False, config=True, help=
299 299 """
300 300 Automatically call the pdb debugger after every exception.
301 301 """
302 302 )
303 303 multiline_history = CBool(sys.platform != 'win32', config=True,
304 304 help="Save multi-line entries as one entry in readline history"
305 305 )
306 306
307 307 # deprecated prompt traits:
308 308
309 309 prompt_in1 = Unicode('In [\\#]: ', config=True,
310 310 help="Deprecated, use PromptManager.in_template")
311 311 prompt_in2 = Unicode(' .\\D.: ', config=True,
312 312 help="Deprecated, use PromptManager.in2_template")
313 313 prompt_out = Unicode('Out[\\#]: ', config=True,
314 314 help="Deprecated, use PromptManager.out_template")
315 315 prompts_pad_left = CBool(True, config=True,
316 316 help="Deprecated, use PromptManager.justify")
317 317
318 318 def _prompt_trait_changed(self, name, old, new):
319 319 table = {
320 320 'prompt_in1' : 'in_template',
321 321 'prompt_in2' : 'in2_template',
322 322 'prompt_out' : 'out_template',
323 323 'prompts_pad_left' : 'justify',
324 324 }
325 325 warn("InteractiveShell.{name} is deprecated, use PromptManager.{newname}\n".format(
326 326 name=name, newname=table[name])
327 327 )
328 328 # protect against weird cases where self.config may not exist:
329 329 if self.config is not None:
330 330 # propagate to corresponding PromptManager trait
331 331 setattr(self.config.PromptManager, table[name], new)
332 332
333 333 _prompt_in1_changed = _prompt_trait_changed
334 334 _prompt_in2_changed = _prompt_trait_changed
335 335 _prompt_out_changed = _prompt_trait_changed
336 336 _prompt_pad_left_changed = _prompt_trait_changed
337 337
338 338 show_rewritten_input = CBool(True, config=True,
339 339 help="Show rewritten input, e.g. for autocall."
340 340 )
341 341
342 342 quiet = CBool(False, config=True)
343 343
344 344 history_length = Integer(10000, config=True)
345 345
346 346 # The readline stuff will eventually be moved to the terminal subclass
347 347 # but for now, we can't do that as readline is welded in everywhere.
348 348 readline_use = CBool(True, config=True)
349 349 readline_remove_delims = Unicode('-/~', config=True)
350 350 # don't use \M- bindings by default, because they
351 351 # conflict with 8-bit encodings. See gh-58,gh-88
352 352 readline_parse_and_bind = List([
353 353 'tab: complete',
354 354 '"\C-l": clear-screen',
355 355 'set show-all-if-ambiguous on',
356 356 '"\C-o": tab-insert',
357 357 '"\C-r": reverse-search-history',
358 358 '"\C-s": forward-search-history',
359 359 '"\C-p": history-search-backward',
360 360 '"\C-n": history-search-forward',
361 361 '"\e[A": history-search-backward',
362 362 '"\e[B": history-search-forward',
363 363 '"\C-k": kill-line',
364 364 '"\C-u": unix-line-discard',
365 365 ], allow_none=False, config=True)
366 366
367 367 ast_node_interactivity = Enum(['all', 'last', 'last_expr', 'none'],
368 368 default_value='last_expr', config=True,
369 369 help="""
370 370 'all', 'last', 'last_expr' or 'none', specifying which nodes should be
371 371 run interactively (displaying output from expressions).""")
372 372
373 373 # TODO: this part of prompt management should be moved to the frontends.
374 374 # Use custom TraitTypes that convert '0'->'' and '\\n'->'\n'
375 375 separate_in = SeparateUnicode('\n', config=True)
376 376 separate_out = SeparateUnicode('', config=True)
377 377 separate_out2 = SeparateUnicode('', config=True)
378 378 wildcards_case_sensitive = CBool(True, config=True)
379 379 xmode = CaselessStrEnum(('Context','Plain', 'Verbose'),
380 380 default_value='Context', config=True)
381 381
382 382 # Subcomponents of InteractiveShell
383 383 alias_manager = Instance('IPython.core.alias.AliasManager')
384 384 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager')
385 385 builtin_trap = Instance('IPython.core.builtin_trap.BuiltinTrap')
386 386 display_trap = Instance('IPython.core.display_trap.DisplayTrap')
387 387 extension_manager = Instance('IPython.core.extensions.ExtensionManager')
388 388 payload_manager = Instance('IPython.core.payload.PayloadManager')
389 389 history_manager = Instance('IPython.core.history.HistoryManager')
390 390 magics_manager = Instance('IPython.core.magic.MagicsManager')
391 391
392 392 profile_dir = Instance('IPython.core.application.ProfileDir')
393 393 @property
394 394 def profile(self):
395 395 if self.profile_dir is not None:
396 396 name = os.path.basename(self.profile_dir.location)
397 397 return name.replace('profile_','')
398 398
399 399
400 400 # Private interface
401 401 _post_execute = Instance(dict)
402 402
403 403 # Tracks any GUI loop loaded for pylab
404 404 pylab_gui_select = None
405 405
406 406 def __init__(self, config=None, ipython_dir=None, profile_dir=None,
407 407 user_module=None, user_ns=None,
408 408 custom_exceptions=((), None)):
409 409
410 410 # This is where traits with a config_key argument are updated
411 411 # from the values on config.
412 412 super(InteractiveShell, self).__init__(config=config)
413 413 self.configurables = [self]
414 414
415 415 # These are relatively independent and stateless
416 416 self.init_ipython_dir(ipython_dir)
417 417 self.init_profile_dir(profile_dir)
418 418 self.init_instance_attrs()
419 419 self.init_environment()
420 420
421 421 # Check if we're in a virtualenv, and set up sys.path.
422 422 self.init_virtualenv()
423 423
424 424 # Create namespaces (user_ns, user_global_ns, etc.)
425 425 self.init_create_namespaces(user_module, user_ns)
426 426 # This has to be done after init_create_namespaces because it uses
427 427 # something in self.user_ns, but before init_sys_modules, which
428 428 # is the first thing to modify sys.
429 429 # TODO: When we override sys.stdout and sys.stderr before this class
430 430 # is created, we are saving the overridden ones here. Not sure if this
431 431 # is what we want to do.
432 432 self.save_sys_module_state()
433 433 self.init_sys_modules()
434 434
435 435 # While we're trying to have each part of the code directly access what
436 436 # it needs without keeping redundant references to objects, we have too
437 437 # much legacy code that expects ip.db to exist.
438 438 self.db = PickleShareDB(os.path.join(self.profile_dir.location, 'db'))
439 439
440 440 self.init_history()
441 441 self.init_encoding()
442 442 self.init_prefilter()
443 443
444 444 self.init_syntax_highlighting()
445 445 self.init_hooks()
446 446 self.init_pushd_popd_magic()
447 447 # self.init_traceback_handlers use to be here, but we moved it below
448 448 # because it and init_io have to come after init_readline.
449 449 self.init_user_ns()
450 450 self.init_logger()
451 451 self.init_alias()
452 452 self.init_builtins()
453 453
454 454 # The following was in post_config_initialization
455 455 self.init_inspector()
456 456 # init_readline() must come before init_io(), because init_io uses
457 457 # readline related things.
458 458 self.init_readline()
459 459 # We save this here in case user code replaces raw_input, but it needs
460 460 # to be after init_readline(), because PyPy's readline works by replacing
461 461 # raw_input.
462 462 if py3compat.PY3:
463 463 self.raw_input_original = input
464 464 else:
465 465 self.raw_input_original = raw_input
466 466 # init_completer must come after init_readline, because it needs to
467 467 # know whether readline is present or not system-wide to configure the
468 468 # completers, since the completion machinery can now operate
469 469 # independently of readline (e.g. over the network)
470 470 self.init_completer()
471 471 # TODO: init_io() needs to happen before init_traceback handlers
472 472 # because the traceback handlers hardcode the stdout/stderr streams.
473 473 # This logic in in debugger.Pdb and should eventually be changed.
474 474 self.init_io()
475 475 self.init_traceback_handlers(custom_exceptions)
476 476 self.init_prompts()
477 477 self.init_display_formatter()
478 478 self.init_display_pub()
479 479 self.init_data_pub()
480 480 self.init_displayhook()
481 481 self.init_reload_doctest()
482 482 self.init_latextool()
483 483 self.init_magics()
484 484 self.init_logstart()
485 485 self.init_pdb()
486 486 self.init_extension_manager()
487 487 self.init_payload()
488 488 self.hooks.late_startup_hook()
489 489 atexit.register(self.atexit_operations)
490 490
491 491 def get_ipython(self):
492 492 """Return the currently running IPython instance."""
493 493 return self
494 494
495 495 #-------------------------------------------------------------------------
496 496 # Trait changed handlers
497 497 #-------------------------------------------------------------------------
498 498
499 499 def _ipython_dir_changed(self, name, new):
500 500 if not os.path.isdir(new):
501 501 os.makedirs(new, mode = 0o777)
502 502
503 503 def set_autoindent(self,value=None):
504 504 """Set the autoindent flag, checking for readline support.
505 505
506 506 If called with no arguments, it acts as a toggle."""
507 507
508 508 if value != 0 and not self.has_readline:
509 509 if os.name == 'posix':
510 510 warn("The auto-indent feature requires the readline library")
511 511 self.autoindent = 0
512 512 return
513 513 if value is None:
514 514 self.autoindent = not self.autoindent
515 515 else:
516 516 self.autoindent = value
517 517
518 518 #-------------------------------------------------------------------------
519 519 # init_* methods called by __init__
520 520 #-------------------------------------------------------------------------
521 521
522 522 def init_ipython_dir(self, ipython_dir):
523 523 if ipython_dir is not None:
524 524 self.ipython_dir = ipython_dir
525 525 return
526 526
527 527 self.ipython_dir = get_ipython_dir()
528 528
529 529 def init_profile_dir(self, profile_dir):
530 530 if profile_dir is not None:
531 531 self.profile_dir = profile_dir
532 532 return
533 533 self.profile_dir =\
534 534 ProfileDir.create_profile_dir_by_name(self.ipython_dir, 'default')
535 535
536 536 def init_instance_attrs(self):
537 537 self.more = False
538 538
539 539 # command compiler
540 540 self.compile = CachingCompiler()
541 541
542 542 # Make an empty namespace, which extension writers can rely on both
543 543 # existing and NEVER being used by ipython itself. This gives them a
544 544 # convenient location for storing additional information and state
545 545 # their extensions may require, without fear of collisions with other
546 546 # ipython names that may develop later.
547 547 self.meta = Struct()
548 548
549 549 # Temporary files used for various purposes. Deleted at exit.
550 550 self.tempfiles = []
551 551
552 552 # Keep track of readline usage (later set by init_readline)
553 553 self.has_readline = False
554 554
555 555 # keep track of where we started running (mainly for crash post-mortem)
556 556 # This is not being used anywhere currently.
557 557 self.starting_dir = os.getcwdu()
558 558
559 559 # Indentation management
560 560 self.indent_current_nsp = 0
561 561
562 562 # Dict to track post-execution functions that have been registered
563 563 self._post_execute = {}
564 564
565 565 def init_environment(self):
566 566 """Any changes we need to make to the user's environment."""
567 567 pass
568 568
569 569 def init_encoding(self):
570 570 # Get system encoding at startup time. Certain terminals (like Emacs
571 571 # under Win32 have it set to None, and we need to have a known valid
572 572 # encoding to use in the raw_input() method
573 573 try:
574 574 self.stdin_encoding = sys.stdin.encoding or 'ascii'
575 575 except AttributeError:
576 576 self.stdin_encoding = 'ascii'
577 577
578 578 def init_syntax_highlighting(self):
579 579 # Python source parser/formatter for syntax highlighting
580 580 pyformat = PyColorize.Parser().format
581 581 self.pycolorize = lambda src: pyformat(src,'str',self.colors)
582 582
583 583 def init_pushd_popd_magic(self):
584 584 # for pushd/popd management
585 585 self.home_dir = get_home_dir()
586 586
587 587 self.dir_stack = []
588 588
589 589 def init_logger(self):
590 590 self.logger = Logger(self.home_dir, logfname='ipython_log.py',
591 591 logmode='rotate')
592 592
593 593 def init_logstart(self):
594 594 """Initialize logging in case it was requested at the command line.
595 595 """
596 596 if self.logappend:
597 597 self.magic('logstart %s append' % self.logappend)
598 598 elif self.logfile:
599 599 self.magic('logstart %s' % self.logfile)
600 600 elif self.logstart:
601 601 self.magic('logstart')
602 602
603 603 def init_builtins(self):
604 604 # A single, static flag that we set to True. Its presence indicates
605 605 # that an IPython shell has been created, and we make no attempts at
606 606 # removing on exit or representing the existence of more than one
607 607 # IPython at a time.
608 608 builtin_mod.__dict__['__IPYTHON__'] = True
609 609
610 610 # In 0.11 we introduced '__IPYTHON__active' as an integer we'd try to
611 611 # manage on enter/exit, but with all our shells it's virtually
612 612 # impossible to get all the cases right. We're leaving the name in for
613 613 # those who adapted their codes to check for this flag, but will
614 614 # eventually remove it after a few more releases.
615 615 builtin_mod.__dict__['__IPYTHON__active'] = \
616 616 'Deprecated, check for __IPYTHON__'
617 617
618 618 self.builtin_trap = BuiltinTrap(shell=self)
619 619
620 620 def init_inspector(self):
621 621 # Object inspector
622 622 self.inspector = oinspect.Inspector(oinspect.InspectColors,
623 623 PyColorize.ANSICodeColors,
624 624 'NoColor',
625 625 self.object_info_string_level)
626 626
627 627 def init_io(self):
628 628 # This will just use sys.stdout and sys.stderr. If you want to
629 629 # override sys.stdout and sys.stderr themselves, you need to do that
630 630 # *before* instantiating this class, because io holds onto
631 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 633 io.stdout = io.stderr = io.IOStream(self.readline._outputfile)
634 634 else:
635 635 io.stdout = io.IOStream(sys.stdout)
636 636 io.stderr = io.IOStream(sys.stderr)
637 637
638 638 def init_prompts(self):
639 639 self.prompt_manager = PromptManager(shell=self, config=self.config)
640 640 self.configurables.append(self.prompt_manager)
641 641 # Set system prompts, so that scripts can decide if they are running
642 642 # interactively.
643 643 sys.ps1 = 'In : '
644 644 sys.ps2 = '...: '
645 645 sys.ps3 = 'Out: '
646 646
647 647 def init_display_formatter(self):
648 648 self.display_formatter = DisplayFormatter(config=self.config)
649 649 self.configurables.append(self.display_formatter)
650 650
651 651 def init_display_pub(self):
652 652 self.display_pub = self.display_pub_class(config=self.config)
653 653 self.configurables.append(self.display_pub)
654 654
655 655 def init_data_pub(self):
656 656 if not self.data_pub_class:
657 657 self.data_pub = None
658 658 return
659 659 self.data_pub = self.data_pub_class(config=self.config)
660 660 self.configurables.append(self.data_pub)
661 661
662 662 def init_displayhook(self):
663 663 # Initialize displayhook, set in/out prompts and printing system
664 664 self.displayhook = self.displayhook_class(
665 665 config=self.config,
666 666 shell=self,
667 667 cache_size=self.cache_size,
668 668 )
669 669 self.configurables.append(self.displayhook)
670 670 # This is a context manager that installs/revmoes the displayhook at
671 671 # the appropriate time.
672 672 self.display_trap = DisplayTrap(hook=self.displayhook)
673 673
674 674 def init_reload_doctest(self):
675 675 # Do a proper resetting of doctest, including the necessary displayhook
676 676 # monkeypatching
677 677 try:
678 678 doctest_reload()
679 679 except ImportError:
680 680 warn("doctest module does not exist.")
681 681
682 682 def init_latextool(self):
683 683 """Configure LaTeXTool."""
684 684 cfg = LaTeXTool.instance(config=self.config)
685 685 if cfg not in self.configurables:
686 686 self.configurables.append(cfg)
687 687
688 688 def init_virtualenv(self):
689 689 """Add a virtualenv to sys.path so the user can import modules from it.
690 690 This isn't perfect: it doesn't use the Python interpreter with which the
691 691 virtualenv was built, and it ignores the --no-site-packages option. A
692 692 warning will appear suggesting the user installs IPython in the
693 693 virtualenv, but for many cases, it probably works well enough.
694 694
695 695 Adapted from code snippets online.
696 696
697 697 http://blog.ufsoft.org/2009/1/29/ipython-and-virtualenv
698 698 """
699 699 if 'VIRTUAL_ENV' not in os.environ:
700 700 # Not in a virtualenv
701 701 return
702 702
703 703 if sys.executable.startswith(os.environ['VIRTUAL_ENV']):
704 704 # Running properly in the virtualenv, don't need to do anything
705 705 return
706 706
707 707 warn("Attempting to work in a virtualenv. If you encounter problems, please "
708 708 "install IPython inside the virtualenv.\n")
709 709 if sys.platform == "win32":
710 710 virtual_env = os.path.join(os.environ['VIRTUAL_ENV'], 'Lib', 'site-packages')
711 711 else:
712 712 virtual_env = os.path.join(os.environ['VIRTUAL_ENV'], 'lib',
713 713 'python%d.%d' % sys.version_info[:2], 'site-packages')
714 714
715 715 import site
716 716 sys.path.insert(0, virtual_env)
717 717 site.addsitedir(virtual_env)
718 718
719 719 #-------------------------------------------------------------------------
720 720 # Things related to injections into the sys module
721 721 #-------------------------------------------------------------------------
722 722
723 723 def save_sys_module_state(self):
724 724 """Save the state of hooks in the sys module.
725 725
726 726 This has to be called after self.user_module is created.
727 727 """
728 728 self._orig_sys_module_state = {}
729 729 self._orig_sys_module_state['stdin'] = sys.stdin
730 730 self._orig_sys_module_state['stdout'] = sys.stdout
731 731 self._orig_sys_module_state['stderr'] = sys.stderr
732 732 self._orig_sys_module_state['excepthook'] = sys.excepthook
733 733 self._orig_sys_modules_main_name = self.user_module.__name__
734 734 self._orig_sys_modules_main_mod = sys.modules.get(self.user_module.__name__)
735 735
736 736 def restore_sys_module_state(self):
737 737 """Restore the state of the sys module."""
738 738 try:
739 739 for k, v in self._orig_sys_module_state.iteritems():
740 740 setattr(sys, k, v)
741 741 except AttributeError:
742 742 pass
743 743 # Reset what what done in self.init_sys_modules
744 744 if self._orig_sys_modules_main_mod is not None:
745 745 sys.modules[self._orig_sys_modules_main_name] = self._orig_sys_modules_main_mod
746 746
747 747 #-------------------------------------------------------------------------
748 748 # Things related to hooks
749 749 #-------------------------------------------------------------------------
750 750
751 751 def init_hooks(self):
752 752 # hooks holds pointers used for user-side customizations
753 753 self.hooks = Struct()
754 754
755 755 self.strdispatchers = {}
756 756
757 757 # Set all default hooks, defined in the IPython.hooks module.
758 758 hooks = IPython.core.hooks
759 759 for hook_name in hooks.__all__:
760 760 # default hooks have priority 100, i.e. low; user hooks should have
761 761 # 0-100 priority
762 762 self.set_hook(hook_name,getattr(hooks,hook_name), 100)
763 763
764 764 def set_hook(self,name,hook, priority = 50, str_key = None, re_key = None):
765 765 """set_hook(name,hook) -> sets an internal IPython hook.
766 766
767 767 IPython exposes some of its internal API as user-modifiable hooks. By
768 768 adding your function to one of these hooks, you can modify IPython's
769 769 behavior to call at runtime your own routines."""
770 770
771 771 # At some point in the future, this should validate the hook before it
772 772 # accepts it. Probably at least check that the hook takes the number
773 773 # of args it's supposed to.
774 774
775 775 f = types.MethodType(hook,self)
776 776
777 777 # check if the hook is for strdispatcher first
778 778 if str_key is not None:
779 779 sdp = self.strdispatchers.get(name, StrDispatch())
780 780 sdp.add_s(str_key, f, priority )
781 781 self.strdispatchers[name] = sdp
782 782 return
783 783 if re_key is not None:
784 784 sdp = self.strdispatchers.get(name, StrDispatch())
785 785 sdp.add_re(re.compile(re_key), f, priority )
786 786 self.strdispatchers[name] = sdp
787 787 return
788 788
789 789 dp = getattr(self.hooks, name, None)
790 790 if name not in IPython.core.hooks.__all__:
791 791 print("Warning! Hook '%s' is not one of %s" % \
792 792 (name, IPython.core.hooks.__all__ ))
793 793 if not dp:
794 794 dp = IPython.core.hooks.CommandChainDispatcher()
795 795
796 796 try:
797 797 dp.add(f,priority)
798 798 except AttributeError:
799 799 # it was not commandchain, plain old func - replace
800 800 dp = f
801 801
802 802 setattr(self.hooks,name, dp)
803 803
804 804 def register_post_execute(self, func):
805 805 """Register a function for calling after code execution.
806 806 """
807 807 if not callable(func):
808 808 raise ValueError('argument %s must be callable' % func)
809 809 self._post_execute[func] = True
810 810
811 811 #-------------------------------------------------------------------------
812 812 # Things related to the "main" module
813 813 #-------------------------------------------------------------------------
814 814
815 815 def new_main_mod(self,ns=None):
816 816 """Return a new 'main' module object for user code execution.
817 817 """
818 818 main_mod = self._user_main_module
819 819 init_fakemod_dict(main_mod,ns)
820 820 return main_mod
821 821
822 822 def cache_main_mod(self,ns,fname):
823 823 """Cache a main module's namespace.
824 824
825 825 When scripts are executed via %run, we must keep a reference to the
826 826 namespace of their __main__ module (a FakeModule instance) around so
827 827 that Python doesn't clear it, rendering objects defined therein
828 828 useless.
829 829
830 830 This method keeps said reference in a private dict, keyed by the
831 831 absolute path of the module object (which corresponds to the script
832 832 path). This way, for multiple executions of the same script we only
833 833 keep one copy of the namespace (the last one), thus preventing memory
834 834 leaks from old references while allowing the objects from the last
835 835 execution to be accessible.
836 836
837 837 Note: we can not allow the actual FakeModule instances to be deleted,
838 838 because of how Python tears down modules (it hard-sets all their
839 839 references to None without regard for reference counts). This method
840 840 must therefore make a *copy* of the given namespace, to allow the
841 841 original module's __dict__ to be cleared and reused.
842 842
843 843
844 844 Parameters
845 845 ----------
846 846 ns : a namespace (a dict, typically)
847 847
848 848 fname : str
849 849 Filename associated with the namespace.
850 850
851 851 Examples
852 852 --------
853 853
854 854 In [10]: import IPython
855 855
856 856 In [11]: _ip.cache_main_mod(IPython.__dict__,IPython.__file__)
857 857
858 858 In [12]: IPython.__file__ in _ip._main_ns_cache
859 859 Out[12]: True
860 860 """
861 861 self._main_ns_cache[os.path.abspath(fname)] = ns.copy()
862 862
863 863 def clear_main_mod_cache(self):
864 864 """Clear the cache of main modules.
865 865
866 866 Mainly for use by utilities like %reset.
867 867
868 868 Examples
869 869 --------
870 870
871 871 In [15]: import IPython
872 872
873 873 In [16]: _ip.cache_main_mod(IPython.__dict__,IPython.__file__)
874 874
875 875 In [17]: len(_ip._main_ns_cache) > 0
876 876 Out[17]: True
877 877
878 878 In [18]: _ip.clear_main_mod_cache()
879 879
880 880 In [19]: len(_ip._main_ns_cache) == 0
881 881 Out[19]: True
882 882 """
883 883 self._main_ns_cache.clear()
884 884
885 885 #-------------------------------------------------------------------------
886 886 # Things related to debugging
887 887 #-------------------------------------------------------------------------
888 888
889 889 def init_pdb(self):
890 890 # Set calling of pdb on exceptions
891 891 # self.call_pdb is a property
892 892 self.call_pdb = self.pdb
893 893
894 894 def _get_call_pdb(self):
895 895 return self._call_pdb
896 896
897 897 def _set_call_pdb(self,val):
898 898
899 899 if val not in (0,1,False,True):
900 900 raise ValueError('new call_pdb value must be boolean')
901 901
902 902 # store value in instance
903 903 self._call_pdb = val
904 904
905 905 # notify the actual exception handlers
906 906 self.InteractiveTB.call_pdb = val
907 907
908 908 call_pdb = property(_get_call_pdb,_set_call_pdb,None,
909 909 'Control auto-activation of pdb at exceptions')
910 910
911 911 def debugger(self,force=False):
912 912 """Call the pydb/pdb debugger.
913 913
914 914 Keywords:
915 915
916 916 - force(False): by default, this routine checks the instance call_pdb
917 917 flag and does not actually invoke the debugger if the flag is false.
918 918 The 'force' option forces the debugger to activate even if the flag
919 919 is false.
920 920 """
921 921
922 922 if not (force or self.call_pdb):
923 923 return
924 924
925 925 if not hasattr(sys,'last_traceback'):
926 926 error('No traceback has been produced, nothing to debug.')
927 927 return
928 928
929 929 # use pydb if available
930 930 if debugger.has_pydb:
931 931 from pydb import pm
932 932 else:
933 933 # fallback to our internal debugger
934 934 pm = lambda : self.InteractiveTB.debugger(force=True)
935 935
936 936 with self.readline_no_record:
937 937 pm()
938 938
939 939 #-------------------------------------------------------------------------
940 940 # Things related to IPython's various namespaces
941 941 #-------------------------------------------------------------------------
942 942 default_user_namespaces = True
943 943
944 944 def init_create_namespaces(self, user_module=None, user_ns=None):
945 945 # Create the namespace where the user will operate. user_ns is
946 946 # normally the only one used, and it is passed to the exec calls as
947 947 # the locals argument. But we do carry a user_global_ns namespace
948 948 # given as the exec 'globals' argument, This is useful in embedding
949 949 # situations where the ipython shell opens in a context where the
950 950 # distinction between locals and globals is meaningful. For
951 951 # non-embedded contexts, it is just the same object as the user_ns dict.
952 952
953 953 # FIXME. For some strange reason, __builtins__ is showing up at user
954 954 # level as a dict instead of a module. This is a manual fix, but I
955 955 # should really track down where the problem is coming from. Alex
956 956 # Schmolck reported this problem first.
957 957
958 958 # A useful post by Alex Martelli on this topic:
959 959 # Re: inconsistent value from __builtins__
960 960 # Von: Alex Martelli <aleaxit@yahoo.com>
961 961 # Datum: Freitag 01 Oktober 2004 04:45:34 nachmittags/abends
962 962 # Gruppen: comp.lang.python
963 963
964 964 # Michael Hohn <hohn@hooknose.lbl.gov> wrote:
965 965 # > >>> print type(builtin_check.get_global_binding('__builtins__'))
966 966 # > <type 'dict'>
967 967 # > >>> print type(__builtins__)
968 968 # > <type 'module'>
969 969 # > Is this difference in return value intentional?
970 970
971 971 # Well, it's documented that '__builtins__' can be either a dictionary
972 972 # or a module, and it's been that way for a long time. Whether it's
973 973 # intentional (or sensible), I don't know. In any case, the idea is
974 974 # that if you need to access the built-in namespace directly, you
975 975 # should start with "import __builtin__" (note, no 's') which will
976 976 # definitely give you a module. Yeah, it's somewhat confusing:-(.
977 977
978 978 # These routines return a properly built module and dict as needed by
979 979 # the rest of the code, and can also be used by extension writers to
980 980 # generate properly initialized namespaces.
981 981 if (user_ns is not None) or (user_module is not None):
982 982 self.default_user_namespaces = False
983 983 self.user_module, self.user_ns = self.prepare_user_module(user_module, user_ns)
984 984
985 985 # A record of hidden variables we have added to the user namespace, so
986 986 # we can list later only variables defined in actual interactive use.
987 987 self.user_ns_hidden = set()
988 988
989 989 # Now that FakeModule produces a real module, we've run into a nasty
990 990 # problem: after script execution (via %run), the module where the user
991 991 # code ran is deleted. Now that this object is a true module (needed
992 992 # so docetst and other tools work correctly), the Python module
993 993 # teardown mechanism runs over it, and sets to None every variable
994 994 # present in that module. Top-level references to objects from the
995 995 # script survive, because the user_ns is updated with them. However,
996 996 # calling functions defined in the script that use other things from
997 997 # the script will fail, because the function's closure had references
998 998 # to the original objects, which are now all None. So we must protect
999 999 # these modules from deletion by keeping a cache.
1000 1000 #
1001 1001 # To avoid keeping stale modules around (we only need the one from the
1002 1002 # last run), we use a dict keyed with the full path to the script, so
1003 1003 # only the last version of the module is held in the cache. Note,
1004 1004 # however, that we must cache the module *namespace contents* (their
1005 1005 # __dict__). Because if we try to cache the actual modules, old ones
1006 1006 # (uncached) could be destroyed while still holding references (such as
1007 1007 # those held by GUI objects that tend to be long-lived)>
1008 1008 #
1009 1009 # The %reset command will flush this cache. See the cache_main_mod()
1010 1010 # and clear_main_mod_cache() methods for details on use.
1011 1011
1012 1012 # This is the cache used for 'main' namespaces
1013 1013 self._main_ns_cache = {}
1014 1014 # And this is the single instance of FakeModule whose __dict__ we keep
1015 1015 # copying and clearing for reuse on each %run
1016 1016 self._user_main_module = FakeModule()
1017 1017
1018 1018 # A table holding all the namespaces IPython deals with, so that
1019 1019 # introspection facilities can search easily.
1020 1020 self.ns_table = {'user_global':self.user_module.__dict__,
1021 1021 'user_local':self.user_ns,
1022 1022 'builtin':builtin_mod.__dict__
1023 1023 }
1024 1024
1025 1025 @property
1026 1026 def user_global_ns(self):
1027 1027 return self.user_module.__dict__
1028 1028
1029 1029 def prepare_user_module(self, user_module=None, user_ns=None):
1030 1030 """Prepare the module and namespace in which user code will be run.
1031 1031
1032 1032 When IPython is started normally, both parameters are None: a new module
1033 1033 is created automatically, and its __dict__ used as the namespace.
1034 1034
1035 1035 If only user_module is provided, its __dict__ is used as the namespace.
1036 1036 If only user_ns is provided, a dummy module is created, and user_ns
1037 1037 becomes the global namespace. If both are provided (as they may be
1038 1038 when embedding), user_ns is the local namespace, and user_module
1039 1039 provides the global namespace.
1040 1040
1041 1041 Parameters
1042 1042 ----------
1043 1043 user_module : module, optional
1044 1044 The current user module in which IPython is being run. If None,
1045 1045 a clean module will be created.
1046 1046 user_ns : dict, optional
1047 1047 A namespace in which to run interactive commands.
1048 1048
1049 1049 Returns
1050 1050 -------
1051 1051 A tuple of user_module and user_ns, each properly initialised.
1052 1052 """
1053 1053 if user_module is None and user_ns is not None:
1054 1054 user_ns.setdefault("__name__", "__main__")
1055 1055 class DummyMod(object):
1056 1056 "A dummy module used for IPython's interactive namespace."
1057 1057 pass
1058 1058 user_module = DummyMod()
1059 1059 user_module.__dict__ = user_ns
1060 1060
1061 1061 if user_module is None:
1062 1062 user_module = types.ModuleType("__main__",
1063 1063 doc="Automatically created module for IPython interactive environment")
1064 1064
1065 1065 # We must ensure that __builtin__ (without the final 's') is always
1066 1066 # available and pointing to the __builtin__ *module*. For more details:
1067 1067 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
1068 1068 user_module.__dict__.setdefault('__builtin__', builtin_mod)
1069 1069 user_module.__dict__.setdefault('__builtins__', builtin_mod)
1070 1070
1071 1071 if user_ns is None:
1072 1072 user_ns = user_module.__dict__
1073 1073
1074 1074 return user_module, user_ns
1075 1075
1076 1076 def init_sys_modules(self):
1077 1077 # We need to insert into sys.modules something that looks like a
1078 1078 # module but which accesses the IPython namespace, for shelve and
1079 1079 # pickle to work interactively. Normally they rely on getting
1080 1080 # everything out of __main__, but for embedding purposes each IPython
1081 1081 # instance has its own private namespace, so we can't go shoving
1082 1082 # everything into __main__.
1083 1083
1084 1084 # note, however, that we should only do this for non-embedded
1085 1085 # ipythons, which really mimic the __main__.__dict__ with their own
1086 1086 # namespace. Embedded instances, on the other hand, should not do
1087 1087 # this because they need to manage the user local/global namespaces
1088 1088 # only, but they live within a 'normal' __main__ (meaning, they
1089 1089 # shouldn't overtake the execution environment of the script they're
1090 1090 # embedded in).
1091 1091
1092 1092 # This is overridden in the InteractiveShellEmbed subclass to a no-op.
1093 1093 main_name = self.user_module.__name__
1094 1094 sys.modules[main_name] = self.user_module
1095 1095
1096 1096 def init_user_ns(self):
1097 1097 """Initialize all user-visible namespaces to their minimum defaults.
1098 1098
1099 1099 Certain history lists are also initialized here, as they effectively
1100 1100 act as user namespaces.
1101 1101
1102 1102 Notes
1103 1103 -----
1104 1104 All data structures here are only filled in, they are NOT reset by this
1105 1105 method. If they were not empty before, data will simply be added to
1106 1106 therm.
1107 1107 """
1108 1108 # This function works in two parts: first we put a few things in
1109 1109 # user_ns, and we sync that contents into user_ns_hidden so that these
1110 1110 # initial variables aren't shown by %who. After the sync, we add the
1111 1111 # rest of what we *do* want the user to see with %who even on a new
1112 1112 # session (probably nothing, so theye really only see their own stuff)
1113 1113
1114 1114 # The user dict must *always* have a __builtin__ reference to the
1115 1115 # Python standard __builtin__ namespace, which must be imported.
1116 1116 # This is so that certain operations in prompt evaluation can be
1117 1117 # reliably executed with builtins. Note that we can NOT use
1118 1118 # __builtins__ (note the 's'), because that can either be a dict or a
1119 1119 # module, and can even mutate at runtime, depending on the context
1120 1120 # (Python makes no guarantees on it). In contrast, __builtin__ is
1121 1121 # always a module object, though it must be explicitly imported.
1122 1122
1123 1123 # For more details:
1124 1124 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
1125 1125 ns = dict()
1126 1126
1127 1127 # Put 'help' in the user namespace
1128 1128 try:
1129 1129 from site import _Helper
1130 1130 ns['help'] = _Helper()
1131 1131 except ImportError:
1132 1132 warn('help() not available - check site.py')
1133 1133
1134 1134 # make global variables for user access to the histories
1135 1135 ns['_ih'] = self.history_manager.input_hist_parsed
1136 1136 ns['_oh'] = self.history_manager.output_hist
1137 1137 ns['_dh'] = self.history_manager.dir_hist
1138 1138
1139 1139 ns['_sh'] = shadowns
1140 1140
1141 1141 # user aliases to input and output histories. These shouldn't show up
1142 1142 # in %who, as they can have very large reprs.
1143 1143 ns['In'] = self.history_manager.input_hist_parsed
1144 1144 ns['Out'] = self.history_manager.output_hist
1145 1145
1146 1146 # Store myself as the public api!!!
1147 1147 ns['get_ipython'] = self.get_ipython
1148 1148
1149 1149 ns['exit'] = self.exiter
1150 1150 ns['quit'] = self.exiter
1151 1151
1152 1152 # Sync what we've added so far to user_ns_hidden so these aren't seen
1153 1153 # by %who
1154 1154 self.user_ns_hidden.update(ns)
1155 1155
1156 1156 # Anything put into ns now would show up in %who. Think twice before
1157 1157 # putting anything here, as we really want %who to show the user their
1158 1158 # stuff, not our variables.
1159 1159
1160 1160 # Finally, update the real user's namespace
1161 1161 self.user_ns.update(ns)
1162 1162
1163 1163 @property
1164 1164 def all_ns_refs(self):
1165 1165 """Get a list of references to all the namespace dictionaries in which
1166 1166 IPython might store a user-created object.
1167 1167
1168 1168 Note that this does not include the displayhook, which also caches
1169 1169 objects from the output."""
1170 1170 return [self.user_ns, self.user_global_ns,
1171 1171 self._user_main_module.__dict__] + self._main_ns_cache.values()
1172 1172
1173 1173 def reset(self, new_session=True):
1174 1174 """Clear all internal namespaces, and attempt to release references to
1175 1175 user objects.
1176 1176
1177 1177 If new_session is True, a new history session will be opened.
1178 1178 """
1179 1179 # Clear histories
1180 1180 self.history_manager.reset(new_session)
1181 1181 # Reset counter used to index all histories
1182 1182 if new_session:
1183 1183 self.execution_count = 1
1184 1184
1185 1185 # Flush cached output items
1186 1186 if self.displayhook.do_full_cache:
1187 1187 self.displayhook.flush()
1188 1188
1189 1189 # The main execution namespaces must be cleared very carefully,
1190 1190 # skipping the deletion of the builtin-related keys, because doing so
1191 1191 # would cause errors in many object's __del__ methods.
1192 1192 if self.user_ns is not self.user_global_ns:
1193 1193 self.user_ns.clear()
1194 1194 ns = self.user_global_ns
1195 1195 drop_keys = set(ns.keys())
1196 1196 drop_keys.discard('__builtin__')
1197 1197 drop_keys.discard('__builtins__')
1198 1198 drop_keys.discard('__name__')
1199 1199 for k in drop_keys:
1200 1200 del ns[k]
1201 1201
1202 1202 self.user_ns_hidden.clear()
1203 1203
1204 1204 # Restore the user namespaces to minimal usability
1205 1205 self.init_user_ns()
1206 1206
1207 1207 # Restore the default and user aliases
1208 1208 self.alias_manager.clear_aliases()
1209 1209 self.alias_manager.init_aliases()
1210 1210
1211 1211 # Flush the private list of module references kept for script
1212 1212 # execution protection
1213 1213 self.clear_main_mod_cache()
1214 1214
1215 1215 # Clear out the namespace from the last %run
1216 1216 self.new_main_mod()
1217 1217
1218 1218 def del_var(self, varname, by_name=False):
1219 1219 """Delete a variable from the various namespaces, so that, as
1220 1220 far as possible, we're not keeping any hidden references to it.
1221 1221
1222 1222 Parameters
1223 1223 ----------
1224 1224 varname : str
1225 1225 The name of the variable to delete.
1226 1226 by_name : bool
1227 1227 If True, delete variables with the given name in each
1228 1228 namespace. If False (default), find the variable in the user
1229 1229 namespace, and delete references to it.
1230 1230 """
1231 1231 if varname in ('__builtin__', '__builtins__'):
1232 1232 raise ValueError("Refusing to delete %s" % varname)
1233 1233
1234 1234 ns_refs = self.all_ns_refs
1235 1235
1236 1236 if by_name: # Delete by name
1237 1237 for ns in ns_refs:
1238 1238 try:
1239 1239 del ns[varname]
1240 1240 except KeyError:
1241 1241 pass
1242 1242 else: # Delete by object
1243 1243 try:
1244 1244 obj = self.user_ns[varname]
1245 1245 except KeyError:
1246 1246 raise NameError("name '%s' is not defined" % varname)
1247 1247 # Also check in output history
1248 1248 ns_refs.append(self.history_manager.output_hist)
1249 1249 for ns in ns_refs:
1250 1250 to_delete = [n for n, o in ns.iteritems() if o is obj]
1251 1251 for name in to_delete:
1252 1252 del ns[name]
1253 1253
1254 1254 # displayhook keeps extra references, but not in a dictionary
1255 1255 for name in ('_', '__', '___'):
1256 1256 if getattr(self.displayhook, name) is obj:
1257 1257 setattr(self.displayhook, name, None)
1258 1258
1259 1259 def reset_selective(self, regex=None):
1260 1260 """Clear selective variables from internal namespaces based on a
1261 1261 specified regular expression.
1262 1262
1263 1263 Parameters
1264 1264 ----------
1265 1265 regex : string or compiled pattern, optional
1266 1266 A regular expression pattern that will be used in searching
1267 1267 variable names in the users namespaces.
1268 1268 """
1269 1269 if regex is not None:
1270 1270 try:
1271 1271 m = re.compile(regex)
1272 1272 except TypeError:
1273 1273 raise TypeError('regex must be a string or compiled pattern')
1274 1274 # Search for keys in each namespace that match the given regex
1275 1275 # If a match is found, delete the key/value pair.
1276 1276 for ns in self.all_ns_refs:
1277 1277 for var in ns:
1278 1278 if m.search(var):
1279 1279 del ns[var]
1280 1280
1281 1281 def push(self, variables, interactive=True):
1282 1282 """Inject a group of variables into the IPython user namespace.
1283 1283
1284 1284 Parameters
1285 1285 ----------
1286 1286 variables : dict, str or list/tuple of str
1287 1287 The variables to inject into the user's namespace. If a dict, a
1288 1288 simple update is done. If a str, the string is assumed to have
1289 1289 variable names separated by spaces. A list/tuple of str can also
1290 1290 be used to give the variable names. If just the variable names are
1291 1291 give (list/tuple/str) then the variable values looked up in the
1292 1292 callers frame.
1293 1293 interactive : bool
1294 1294 If True (default), the variables will be listed with the ``who``
1295 1295 magic.
1296 1296 """
1297 1297 vdict = None
1298 1298
1299 1299 # We need a dict of name/value pairs to do namespace updates.
1300 1300 if isinstance(variables, dict):
1301 1301 vdict = variables
1302 1302 elif isinstance(variables, (basestring, list, tuple)):
1303 1303 if isinstance(variables, basestring):
1304 1304 vlist = variables.split()
1305 1305 else:
1306 1306 vlist = variables
1307 1307 vdict = {}
1308 1308 cf = sys._getframe(1)
1309 1309 for name in vlist:
1310 1310 try:
1311 1311 vdict[name] = eval(name, cf.f_globals, cf.f_locals)
1312 1312 except:
1313 1313 print('Could not get variable %s from %s' %
1314 1314 (name,cf.f_code.co_name))
1315 1315 else:
1316 1316 raise ValueError('variables must be a dict/str/list/tuple')
1317 1317
1318 1318 # Propagate variables to user namespace
1319 1319 self.user_ns.update(vdict)
1320 1320
1321 1321 # And configure interactive visibility
1322 1322 user_ns_hidden = self.user_ns_hidden
1323 1323 if interactive:
1324 1324 user_ns_hidden.difference_update(vdict)
1325 1325 else:
1326 1326 user_ns_hidden.update(vdict)
1327 1327
1328 1328 def drop_by_id(self, variables):
1329 1329 """Remove a dict of variables from the user namespace, if they are the
1330 1330 same as the values in the dictionary.
1331 1331
1332 1332 This is intended for use by extensions: variables that they've added can
1333 1333 be taken back out if they are unloaded, without removing any that the
1334 1334 user has overwritten.
1335 1335
1336 1336 Parameters
1337 1337 ----------
1338 1338 variables : dict
1339 1339 A dictionary mapping object names (as strings) to the objects.
1340 1340 """
1341 1341 for name, obj in variables.iteritems():
1342 1342 if name in self.user_ns and self.user_ns[name] is obj:
1343 1343 del self.user_ns[name]
1344 1344 self.user_ns_hidden.discard(name)
1345 1345
1346 1346 #-------------------------------------------------------------------------
1347 1347 # Things related to object introspection
1348 1348 #-------------------------------------------------------------------------
1349 1349
1350 1350 def _ofind(self, oname, namespaces=None):
1351 1351 """Find an object in the available namespaces.
1352 1352
1353 1353 self._ofind(oname) -> dict with keys: found,obj,ospace,ismagic
1354 1354
1355 1355 Has special code to detect magic functions.
1356 1356 """
1357 1357 oname = oname.strip()
1358 1358 #print '1- oname: <%r>' % oname # dbg
1359 1359 if not oname.startswith(ESC_MAGIC) and \
1360 1360 not oname.startswith(ESC_MAGIC2) and \
1361 1361 not py3compat.isidentifier(oname, dotted=True):
1362 1362 return dict(found=False)
1363 1363
1364 1364 alias_ns = None
1365 1365 if namespaces is None:
1366 1366 # Namespaces to search in:
1367 1367 # Put them in a list. The order is important so that we
1368 1368 # find things in the same order that Python finds them.
1369 1369 namespaces = [ ('Interactive', self.user_ns),
1370 1370 ('Interactive (global)', self.user_global_ns),
1371 1371 ('Python builtin', builtin_mod.__dict__),
1372 1372 ('Alias', self.alias_manager.alias_table),
1373 1373 ]
1374 1374 alias_ns = self.alias_manager.alias_table
1375 1375
1376 1376 # initialize results to 'null'
1377 1377 found = False; obj = None; ospace = None; ds = None;
1378 1378 ismagic = False; isalias = False; parent = None
1379 1379
1380 1380 # We need to special-case 'print', which as of python2.6 registers as a
1381 1381 # function but should only be treated as one if print_function was
1382 1382 # loaded with a future import. In this case, just bail.
1383 1383 if (oname == 'print' and not py3compat.PY3 and not \
1384 1384 (self.compile.compiler_flags & __future__.CO_FUTURE_PRINT_FUNCTION)):
1385 1385 return {'found':found, 'obj':obj, 'namespace':ospace,
1386 1386 'ismagic':ismagic, 'isalias':isalias, 'parent':parent}
1387 1387
1388 1388 # Look for the given name by splitting it in parts. If the head is
1389 1389 # found, then we look for all the remaining parts as members, and only
1390 1390 # declare success if we can find them all.
1391 1391 oname_parts = oname.split('.')
1392 1392 oname_head, oname_rest = oname_parts[0],oname_parts[1:]
1393 1393 for nsname,ns in namespaces:
1394 1394 try:
1395 1395 obj = ns[oname_head]
1396 1396 except KeyError:
1397 1397 continue
1398 1398 else:
1399 1399 #print 'oname_rest:', oname_rest # dbg
1400 1400 for part in oname_rest:
1401 1401 try:
1402 1402 parent = obj
1403 1403 obj = getattr(obj,part)
1404 1404 except:
1405 1405 # Blanket except b/c some badly implemented objects
1406 1406 # allow __getattr__ to raise exceptions other than
1407 1407 # AttributeError, which then crashes IPython.
1408 1408 break
1409 1409 else:
1410 1410 # If we finish the for loop (no break), we got all members
1411 1411 found = True
1412 1412 ospace = nsname
1413 1413 if ns == alias_ns:
1414 1414 isalias = True
1415 1415 break # namespace loop
1416 1416
1417 1417 # Try to see if it's magic
1418 1418 if not found:
1419 1419 obj = None
1420 1420 if oname.startswith(ESC_MAGIC2):
1421 1421 oname = oname.lstrip(ESC_MAGIC2)
1422 1422 obj = self.find_cell_magic(oname)
1423 1423 elif oname.startswith(ESC_MAGIC):
1424 1424 oname = oname.lstrip(ESC_MAGIC)
1425 1425 obj = self.find_line_magic(oname)
1426 1426 else:
1427 1427 # search without prefix, so run? will find %run?
1428 1428 obj = self.find_line_magic(oname)
1429 1429 if obj is None:
1430 1430 obj = self.find_cell_magic(oname)
1431 1431 if obj is not None:
1432 1432 found = True
1433 1433 ospace = 'IPython internal'
1434 1434 ismagic = True
1435 1435
1436 1436 # Last try: special-case some literals like '', [], {}, etc:
1437 1437 if not found and oname_head in ["''",'""','[]','{}','()']:
1438 1438 obj = eval(oname_head)
1439 1439 found = True
1440 1440 ospace = 'Interactive'
1441 1441
1442 1442 return {'found':found, 'obj':obj, 'namespace':ospace,
1443 1443 'ismagic':ismagic, 'isalias':isalias, 'parent':parent}
1444 1444
1445 1445 def _ofind_property(self, oname, info):
1446 1446 """Second part of object finding, to look for property details."""
1447 1447 if info.found:
1448 1448 # Get the docstring of the class property if it exists.
1449 1449 path = oname.split('.')
1450 1450 root = '.'.join(path[:-1])
1451 1451 if info.parent is not None:
1452 1452 try:
1453 1453 target = getattr(info.parent, '__class__')
1454 1454 # The object belongs to a class instance.
1455 1455 try:
1456 1456 target = getattr(target, path[-1])
1457 1457 # The class defines the object.
1458 1458 if isinstance(target, property):
1459 1459 oname = root + '.__class__.' + path[-1]
1460 1460 info = Struct(self._ofind(oname))
1461 1461 except AttributeError: pass
1462 1462 except AttributeError: pass
1463 1463
1464 1464 # We return either the new info or the unmodified input if the object
1465 1465 # hadn't been found
1466 1466 return info
1467 1467
1468 1468 def _object_find(self, oname, namespaces=None):
1469 1469 """Find an object and return a struct with info about it."""
1470 1470 inf = Struct(self._ofind(oname, namespaces))
1471 1471 return Struct(self._ofind_property(oname, inf))
1472 1472
1473 1473 def _inspect(self, meth, oname, namespaces=None, **kw):
1474 1474 """Generic interface to the inspector system.
1475 1475
1476 1476 This function is meant to be called by pdef, pdoc & friends."""
1477 1477 info = self._object_find(oname, namespaces)
1478 1478 if info.found:
1479 1479 pmethod = getattr(self.inspector, meth)
1480 1480 formatter = format_screen if info.ismagic else None
1481 1481 if meth == 'pdoc':
1482 1482 pmethod(info.obj, oname, formatter)
1483 1483 elif meth == 'pinfo':
1484 1484 pmethod(info.obj, oname, formatter, info, **kw)
1485 1485 else:
1486 1486 pmethod(info.obj, oname)
1487 1487 else:
1488 1488 print('Object `%s` not found.' % oname)
1489 1489 return 'not found' # so callers can take other action
1490 1490
1491 1491 def object_inspect(self, oname, detail_level=0):
1492 1492 with self.builtin_trap:
1493 1493 info = self._object_find(oname)
1494 1494 if info.found:
1495 1495 return self.inspector.info(info.obj, oname, info=info,
1496 1496 detail_level=detail_level
1497 1497 )
1498 1498 else:
1499 1499 return oinspect.object_info(name=oname, found=False)
1500 1500
1501 1501 #-------------------------------------------------------------------------
1502 1502 # Things related to history management
1503 1503 #-------------------------------------------------------------------------
1504 1504
1505 1505 def init_history(self):
1506 1506 """Sets up the command history, and starts regular autosaves."""
1507 1507 self.history_manager = HistoryManager(shell=self, config=self.config)
1508 1508 self.configurables.append(self.history_manager)
1509 1509
1510 1510 #-------------------------------------------------------------------------
1511 1511 # Things related to exception handling and tracebacks (not debugging)
1512 1512 #-------------------------------------------------------------------------
1513 1513
1514 1514 def init_traceback_handlers(self, custom_exceptions):
1515 1515 # Syntax error handler.
1516 1516 self.SyntaxTB = ultratb.SyntaxTB(color_scheme='NoColor')
1517 1517
1518 1518 # The interactive one is initialized with an offset, meaning we always
1519 1519 # want to remove the topmost item in the traceback, which is our own
1520 1520 # internal code. Valid modes: ['Plain','Context','Verbose']
1521 1521 self.InteractiveTB = ultratb.AutoFormattedTB(mode = 'Plain',
1522 1522 color_scheme='NoColor',
1523 1523 tb_offset = 1,
1524 1524 check_cache=self.compile.check_cache)
1525 1525
1526 1526 # The instance will store a pointer to the system-wide exception hook,
1527 1527 # so that runtime code (such as magics) can access it. This is because
1528 1528 # during the read-eval loop, it may get temporarily overwritten.
1529 1529 self.sys_excepthook = sys.excepthook
1530 1530
1531 1531 # and add any custom exception handlers the user may have specified
1532 1532 self.set_custom_exc(*custom_exceptions)
1533 1533
1534 1534 # Set the exception mode
1535 1535 self.InteractiveTB.set_mode(mode=self.xmode)
1536 1536
1537 1537 def set_custom_exc(self, exc_tuple, handler):
1538 1538 """set_custom_exc(exc_tuple,handler)
1539 1539
1540 1540 Set a custom exception handler, which will be called if any of the
1541 1541 exceptions in exc_tuple occur in the mainloop (specifically, in the
1542 1542 run_code() method).
1543 1543
1544 1544 Parameters
1545 1545 ----------
1546 1546
1547 1547 exc_tuple : tuple of exception classes
1548 1548 A *tuple* of exception classes, for which to call the defined
1549 1549 handler. It is very important that you use a tuple, and NOT A
1550 1550 LIST here, because of the way Python's except statement works. If
1551 1551 you only want to trap a single exception, use a singleton tuple::
1552 1552
1553 1553 exc_tuple == (MyCustomException,)
1554 1554
1555 1555 handler : callable
1556 1556 handler must have the following signature::
1557 1557
1558 1558 def my_handler(self, etype, value, tb, tb_offset=None):
1559 1559 ...
1560 1560 return structured_traceback
1561 1561
1562 1562 Your handler must return a structured traceback (a list of strings),
1563 1563 or None.
1564 1564
1565 1565 This will be made into an instance method (via types.MethodType)
1566 1566 of IPython itself, and it will be called if any of the exceptions
1567 1567 listed in the exc_tuple are caught. If the handler is None, an
1568 1568 internal basic one is used, which just prints basic info.
1569 1569
1570 1570 To protect IPython from crashes, if your handler ever raises an
1571 1571 exception or returns an invalid result, it will be immediately
1572 1572 disabled.
1573 1573
1574 1574 WARNING: by putting in your own exception handler into IPython's main
1575 1575 execution loop, you run a very good chance of nasty crashes. This
1576 1576 facility should only be used if you really know what you are doing."""
1577 1577
1578 1578 assert type(exc_tuple)==type(()) , \
1579 1579 "The custom exceptions must be given AS A TUPLE."
1580 1580
1581 1581 def dummy_handler(self,etype,value,tb,tb_offset=None):
1582 1582 print('*** Simple custom exception handler ***')
1583 1583 print('Exception type :',etype)
1584 1584 print('Exception value:',value)
1585 1585 print('Traceback :',tb)
1586 1586 #print 'Source code :','\n'.join(self.buffer)
1587 1587
1588 1588 def validate_stb(stb):
1589 1589 """validate structured traceback return type
1590 1590
1591 1591 return type of CustomTB *should* be a list of strings, but allow
1592 1592 single strings or None, which are harmless.
1593 1593
1594 1594 This function will *always* return a list of strings,
1595 1595 and will raise a TypeError if stb is inappropriate.
1596 1596 """
1597 1597 msg = "CustomTB must return list of strings, not %r" % stb
1598 1598 if stb is None:
1599 1599 return []
1600 1600 elif isinstance(stb, basestring):
1601 1601 return [stb]
1602 1602 elif not isinstance(stb, list):
1603 1603 raise TypeError(msg)
1604 1604 # it's a list
1605 1605 for line in stb:
1606 1606 # check every element
1607 1607 if not isinstance(line, basestring):
1608 1608 raise TypeError(msg)
1609 1609 return stb
1610 1610
1611 1611 if handler is None:
1612 1612 wrapped = dummy_handler
1613 1613 else:
1614 1614 def wrapped(self,etype,value,tb,tb_offset=None):
1615 1615 """wrap CustomTB handler, to protect IPython from user code
1616 1616
1617 1617 This makes it harder (but not impossible) for custom exception
1618 1618 handlers to crash IPython.
1619 1619 """
1620 1620 try:
1621 1621 stb = handler(self,etype,value,tb,tb_offset=tb_offset)
1622 1622 return validate_stb(stb)
1623 1623 except:
1624 1624 # clear custom handler immediately
1625 1625 self.set_custom_exc((), None)
1626 1626 print("Custom TB Handler failed, unregistering", file=io.stderr)
1627 1627 # show the exception in handler first
1628 1628 stb = self.InteractiveTB.structured_traceback(*sys.exc_info())
1629 1629 print(self.InteractiveTB.stb2text(stb), file=io.stdout)
1630 1630 print("The original exception:", file=io.stdout)
1631 1631 stb = self.InteractiveTB.structured_traceback(
1632 1632 (etype,value,tb), tb_offset=tb_offset
1633 1633 )
1634 1634 return stb
1635 1635
1636 1636 self.CustomTB = types.MethodType(wrapped,self)
1637 1637 self.custom_exceptions = exc_tuple
1638 1638
1639 1639 def excepthook(self, etype, value, tb):
1640 1640 """One more defense for GUI apps that call sys.excepthook.
1641 1641
1642 1642 GUI frameworks like wxPython trap exceptions and call
1643 1643 sys.excepthook themselves. I guess this is a feature that
1644 1644 enables them to keep running after exceptions that would
1645 1645 otherwise kill their mainloop. This is a bother for IPython
1646 1646 which excepts to catch all of the program exceptions with a try:
1647 1647 except: statement.
1648 1648
1649 1649 Normally, IPython sets sys.excepthook to a CrashHandler instance, so if
1650 1650 any app directly invokes sys.excepthook, it will look to the user like
1651 1651 IPython crashed. In order to work around this, we can disable the
1652 1652 CrashHandler and replace it with this excepthook instead, which prints a
1653 1653 regular traceback using our InteractiveTB. In this fashion, apps which
1654 1654 call sys.excepthook will generate a regular-looking exception from
1655 1655 IPython, and the CrashHandler will only be triggered by real IPython
1656 1656 crashes.
1657 1657
1658 1658 This hook should be used sparingly, only in places which are not likely
1659 1659 to be true IPython errors.
1660 1660 """
1661 1661 self.showtraceback((etype,value,tb),tb_offset=0)
1662 1662
1663 1663 def _get_exc_info(self, exc_tuple=None):
1664 1664 """get exc_info from a given tuple, sys.exc_info() or sys.last_type etc.
1665 1665
1666 1666 Ensures sys.last_type,value,traceback hold the exc_info we found,
1667 1667 from whichever source.
1668 1668
1669 1669 raises ValueError if none of these contain any information
1670 1670 """
1671 1671 if exc_tuple is None:
1672 1672 etype, value, tb = sys.exc_info()
1673 1673 else:
1674 1674 etype, value, tb = exc_tuple
1675 1675
1676 1676 if etype is None:
1677 1677 if hasattr(sys, 'last_type'):
1678 1678 etype, value, tb = sys.last_type, sys.last_value, \
1679 1679 sys.last_traceback
1680 1680
1681 1681 if etype is None:
1682 1682 raise ValueError("No exception to find")
1683 1683
1684 1684 # Now store the exception info in sys.last_type etc.
1685 1685 # WARNING: these variables are somewhat deprecated and not
1686 1686 # necessarily safe to use in a threaded environment, but tools
1687 1687 # like pdb depend on their existence, so let's set them. If we
1688 1688 # find problems in the field, we'll need to revisit their use.
1689 1689 sys.last_type = etype
1690 1690 sys.last_value = value
1691 1691 sys.last_traceback = tb
1692 1692
1693 1693 return etype, value, tb
1694 1694
1695 1695
1696 1696 def showtraceback(self,exc_tuple = None,filename=None,tb_offset=None,
1697 1697 exception_only=False):
1698 1698 """Display the exception that just occurred.
1699 1699
1700 1700 If nothing is known about the exception, this is the method which
1701 1701 should be used throughout the code for presenting user tracebacks,
1702 1702 rather than directly invoking the InteractiveTB object.
1703 1703
1704 1704 A specific showsyntaxerror() also exists, but this method can take
1705 1705 care of calling it if needed, so unless you are explicitly catching a
1706 1706 SyntaxError exception, don't try to analyze the stack manually and
1707 1707 simply call this method."""
1708 1708
1709 1709 try:
1710 1710 try:
1711 1711 etype, value, tb = self._get_exc_info(exc_tuple)
1712 1712 except ValueError:
1713 1713 self.write_err('No traceback available to show.\n')
1714 1714 return
1715 1715
1716 1716 if issubclass(etype, SyntaxError):
1717 1717 # Though this won't be called by syntax errors in the input
1718 1718 # line, there may be SyntaxError cases with imported code.
1719 1719 self.showsyntaxerror(filename)
1720 1720 elif etype is UsageError:
1721 1721 self.write_err("UsageError: %s" % value)
1722 1722 else:
1723 1723 if exception_only:
1724 1724 stb = ['An exception has occurred, use %tb to see '
1725 1725 'the full traceback.\n']
1726 1726 stb.extend(self.InteractiveTB.get_exception_only(etype,
1727 1727 value))
1728 1728 else:
1729 1729 try:
1730 1730 # Exception classes can customise their traceback - we
1731 1731 # use this in IPython.parallel for exceptions occurring
1732 1732 # in the engines. This should return a list of strings.
1733 1733 stb = value._render_traceback_()
1734 1734 except Exception:
1735 1735 stb = self.InteractiveTB.structured_traceback(etype,
1736 1736 value, tb, tb_offset=tb_offset)
1737 1737
1738 1738 self._showtraceback(etype, value, stb)
1739 1739 if self.call_pdb:
1740 1740 # drop into debugger
1741 1741 self.debugger(force=True)
1742 1742 return
1743 1743
1744 1744 # Actually show the traceback
1745 1745 self._showtraceback(etype, value, stb)
1746 1746
1747 1747 except KeyboardInterrupt:
1748 1748 self.write_err("\nKeyboardInterrupt\n")
1749 1749
1750 1750 def _showtraceback(self, etype, evalue, stb):
1751 1751 """Actually show a traceback.
1752 1752
1753 1753 Subclasses may override this method to put the traceback on a different
1754 1754 place, like a side channel.
1755 1755 """
1756 1756 print(self.InteractiveTB.stb2text(stb), file=io.stdout)
1757 1757
1758 1758 def showsyntaxerror(self, filename=None):
1759 1759 """Display the syntax error that just occurred.
1760 1760
1761 1761 This doesn't display a stack trace because there isn't one.
1762 1762
1763 1763 If a filename is given, it is stuffed in the exception instead
1764 1764 of what was there before (because Python's parser always uses
1765 1765 "<string>" when reading from a string).
1766 1766 """
1767 1767 etype, value, last_traceback = self._get_exc_info()
1768 1768
1769 1769 if filename and issubclass(etype, SyntaxError):
1770 1770 try:
1771 1771 value.filename = filename
1772 1772 except:
1773 1773 # Not the format we expect; leave it alone
1774 1774 pass
1775 1775
1776 1776 stb = self.SyntaxTB.structured_traceback(etype, value, [])
1777 1777 self._showtraceback(etype, value, stb)
1778 1778
1779 1779 # This is overridden in TerminalInteractiveShell to show a message about
1780 1780 # the %paste magic.
1781 1781 def showindentationerror(self):
1782 1782 """Called by run_cell when there's an IndentationError in code entered
1783 1783 at the prompt.
1784 1784
1785 1785 This is overridden in TerminalInteractiveShell to show a message about
1786 1786 the %paste magic."""
1787 1787 self.showsyntaxerror()
1788 1788
1789 1789 #-------------------------------------------------------------------------
1790 1790 # Things related to readline
1791 1791 #-------------------------------------------------------------------------
1792 1792
1793 1793 def init_readline(self):
1794 1794 """Command history completion/saving/reloading."""
1795 1795
1796 1796 if self.readline_use:
1797 1797 import IPython.utils.rlineimpl as readline
1798 1798
1799 1799 self.rl_next_input = None
1800 1800 self.rl_do_indent = False
1801 1801
1802 1802 if not self.readline_use or not readline.have_readline:
1803 1803 self.has_readline = False
1804 1804 self.readline = None
1805 1805 # Set a number of methods that depend on readline to be no-op
1806 1806 self.readline_no_record = no_op_context
1807 1807 self.set_readline_completer = no_op
1808 1808 self.set_custom_completer = no_op
1809 1809 if self.readline_use:
1810 1810 warn('Readline services not available or not loaded.')
1811 1811 else:
1812 1812 self.has_readline = True
1813 1813 self.readline = readline
1814 1814 sys.modules['readline'] = readline
1815 1815
1816 1816 # Platform-specific configuration
1817 1817 if os.name == 'nt':
1818 1818 # FIXME - check with Frederick to see if we can harmonize
1819 1819 # naming conventions with pyreadline to avoid this
1820 1820 # platform-dependent check
1821 1821 self.readline_startup_hook = readline.set_pre_input_hook
1822 1822 else:
1823 1823 self.readline_startup_hook = readline.set_startup_hook
1824 1824
1825 1825 # Load user's initrc file (readline config)
1826 1826 # Or if libedit is used, load editrc.
1827 1827 inputrc_name = os.environ.get('INPUTRC')
1828 1828 if inputrc_name is None:
1829 1829 inputrc_name = '.inputrc'
1830 1830 if readline.uses_libedit:
1831 1831 inputrc_name = '.editrc'
1832 1832 inputrc_name = os.path.join(self.home_dir, inputrc_name)
1833 1833 if os.path.isfile(inputrc_name):
1834 1834 try:
1835 1835 readline.read_init_file(inputrc_name)
1836 1836 except:
1837 1837 warn('Problems reading readline initialization file <%s>'
1838 1838 % inputrc_name)
1839 1839
1840 1840 # Configure readline according to user's prefs
1841 1841 # This is only done if GNU readline is being used. If libedit
1842 1842 # is being used (as on Leopard) the readline config is
1843 1843 # not run as the syntax for libedit is different.
1844 1844 if not readline.uses_libedit:
1845 1845 for rlcommand in self.readline_parse_and_bind:
1846 1846 #print "loading rl:",rlcommand # dbg
1847 1847 readline.parse_and_bind(rlcommand)
1848 1848
1849 1849 # Remove some chars from the delimiters list. If we encounter
1850 1850 # unicode chars, discard them.
1851 1851 delims = readline.get_completer_delims()
1852 1852 if not py3compat.PY3:
1853 1853 delims = delims.encode("ascii", "ignore")
1854 1854 for d in self.readline_remove_delims:
1855 1855 delims = delims.replace(d, "")
1856 1856 delims = delims.replace(ESC_MAGIC, '')
1857 1857 readline.set_completer_delims(delims)
1858 1858 # otherwise we end up with a monster history after a while:
1859 1859 readline.set_history_length(self.history_length)
1860 1860
1861 1861 self.refill_readline_hist()
1862 1862 self.readline_no_record = ReadlineNoRecord(self)
1863 1863
1864 1864 # Configure auto-indent for all platforms
1865 1865 self.set_autoindent(self.autoindent)
1866 1866
1867 1867 def refill_readline_hist(self):
1868 1868 # Load the last 1000 lines from history
1869 1869 self.readline.clear_history()
1870 1870 stdin_encoding = sys.stdin.encoding or "utf-8"
1871 1871 last_cell = u""
1872 1872 for _, _, cell in self.history_manager.get_tail(1000,
1873 1873 include_latest=True):
1874 1874 # Ignore blank lines and consecutive duplicates
1875 1875 cell = cell.rstrip()
1876 1876 if cell and (cell != last_cell):
1877 1877 if self.multiline_history:
1878 1878 self.readline.add_history(py3compat.unicode_to_str(cell,
1879 1879 stdin_encoding))
1880 1880 else:
1881 1881 for line in cell.splitlines():
1882 1882 self.readline.add_history(py3compat.unicode_to_str(line,
1883 1883 stdin_encoding))
1884 1884 last_cell = cell
1885 1885
1886 1886 def set_next_input(self, s):
1887 1887 """ Sets the 'default' input string for the next command line.
1888 1888
1889 1889 Requires readline.
1890 1890
1891 1891 Example:
1892 1892
1893 1893 [D:\ipython]|1> _ip.set_next_input("Hello Word")
1894 1894 [D:\ipython]|2> Hello Word_ # cursor is here
1895 1895 """
1896 1896 self.rl_next_input = py3compat.cast_bytes_py2(s)
1897 1897
1898 1898 # Maybe move this to the terminal subclass?
1899 1899 def pre_readline(self):
1900 1900 """readline hook to be used at the start of each line.
1901 1901
1902 1902 Currently it handles auto-indent only."""
1903 1903
1904 1904 if self.rl_do_indent:
1905 1905 self.readline.insert_text(self._indent_current_str())
1906 1906 if self.rl_next_input is not None:
1907 1907 self.readline.insert_text(self.rl_next_input)
1908 1908 self.rl_next_input = None
1909 1909
1910 1910 def _indent_current_str(self):
1911 1911 """return the current level of indentation as a string"""
1912 1912 return self.input_splitter.indent_spaces * ' '
1913 1913
1914 1914 #-------------------------------------------------------------------------
1915 1915 # Things related to text completion
1916 1916 #-------------------------------------------------------------------------
1917 1917
1918 1918 def init_completer(self):
1919 1919 """Initialize the completion machinery.
1920 1920
1921 1921 This creates completion machinery that can be used by client code,
1922 1922 either interactively in-process (typically triggered by the readline
1923 1923 library), programatically (such as in test suites) or out-of-prcess
1924 1924 (typically over the network by remote frontends).
1925 1925 """
1926 1926 from IPython.core.completer import IPCompleter
1927 1927 from IPython.core.completerlib import (module_completer,
1928 1928 magic_run_completer, cd_completer, reset_completer)
1929 1929
1930 1930 self.Completer = IPCompleter(shell=self,
1931 1931 namespace=self.user_ns,
1932 1932 global_namespace=self.user_global_ns,
1933 1933 alias_table=self.alias_manager.alias_table,
1934 1934 use_readline=self.has_readline,
1935 1935 config=self.config,
1936 1936 )
1937 1937 self.configurables.append(self.Completer)
1938 1938
1939 1939 # Add custom completers to the basic ones built into IPCompleter
1940 1940 sdisp = self.strdispatchers.get('complete_command', StrDispatch())
1941 1941 self.strdispatchers['complete_command'] = sdisp
1942 1942 self.Completer.custom_completers = sdisp
1943 1943
1944 1944 self.set_hook('complete_command', module_completer, str_key = 'import')
1945 1945 self.set_hook('complete_command', module_completer, str_key = 'from')
1946 1946 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
1947 1947 self.set_hook('complete_command', cd_completer, str_key = '%cd')
1948 1948 self.set_hook('complete_command', reset_completer, str_key = '%reset')
1949 1949
1950 1950 # Only configure readline if we truly are using readline. IPython can
1951 1951 # do tab-completion over the network, in GUIs, etc, where readline
1952 1952 # itself may be absent
1953 1953 if self.has_readline:
1954 1954 self.set_readline_completer()
1955 1955
1956 1956 def complete(self, text, line=None, cursor_pos=None):
1957 1957 """Return the completed text and a list of completions.
1958 1958
1959 1959 Parameters
1960 1960 ----------
1961 1961
1962 1962 text : string
1963 1963 A string of text to be completed on. It can be given as empty and
1964 1964 instead a line/position pair are given. In this case, the
1965 1965 completer itself will split the line like readline does.
1966 1966
1967 1967 line : string, optional
1968 1968 The complete line that text is part of.
1969 1969
1970 1970 cursor_pos : int, optional
1971 1971 The position of the cursor on the input line.
1972 1972
1973 1973 Returns
1974 1974 -------
1975 1975 text : string
1976 1976 The actual text that was completed.
1977 1977
1978 1978 matches : list
1979 1979 A sorted list with all possible completions.
1980 1980
1981 1981 The optional arguments allow the completion to take more context into
1982 1982 account, and are part of the low-level completion API.
1983 1983
1984 1984 This is a wrapper around the completion mechanism, similar to what
1985 1985 readline does at the command line when the TAB key is hit. By
1986 1986 exposing it as a method, it can be used by other non-readline
1987 1987 environments (such as GUIs) for text completion.
1988 1988
1989 1989 Simple usage example:
1990 1990
1991 1991 In [1]: x = 'hello'
1992 1992
1993 1993 In [2]: _ip.complete('x.l')
1994 1994 Out[2]: ('x.l', ['x.ljust', 'x.lower', 'x.lstrip'])
1995 1995 """
1996 1996
1997 1997 # Inject names into __builtin__ so we can complete on the added names.
1998 1998 with self.builtin_trap:
1999 1999 return self.Completer.complete(text, line, cursor_pos)
2000 2000
2001 2001 def set_custom_completer(self, completer, pos=0):
2002 2002 """Adds a new custom completer function.
2003 2003
2004 2004 The position argument (defaults to 0) is the index in the completers
2005 2005 list where you want the completer to be inserted."""
2006 2006
2007 2007 newcomp = types.MethodType(completer,self.Completer)
2008 2008 self.Completer.matchers.insert(pos,newcomp)
2009 2009
2010 2010 def set_readline_completer(self):
2011 2011 """Reset readline's completer to be our own."""
2012 2012 self.readline.set_completer(self.Completer.rlcomplete)
2013 2013
2014 2014 def set_completer_frame(self, frame=None):
2015 2015 """Set the frame of the completer."""
2016 2016 if frame:
2017 2017 self.Completer.namespace = frame.f_locals
2018 2018 self.Completer.global_namespace = frame.f_globals
2019 2019 else:
2020 2020 self.Completer.namespace = self.user_ns
2021 2021 self.Completer.global_namespace = self.user_global_ns
2022 2022
2023 2023 #-------------------------------------------------------------------------
2024 2024 # Things related to magics
2025 2025 #-------------------------------------------------------------------------
2026 2026
2027 2027 def init_magics(self):
2028 2028 from IPython.core import magics as m
2029 2029 self.magics_manager = magic.MagicsManager(shell=self,
2030 2030 confg=self.config,
2031 2031 user_magics=m.UserMagics(self))
2032 2032 self.configurables.append(self.magics_manager)
2033 2033
2034 2034 # Expose as public API from the magics manager
2035 2035 self.register_magics = self.magics_manager.register
2036 2036 self.register_magic_function = self.magics_manager.register_function
2037 2037 self.define_magic = self.magics_manager.define_magic
2038 2038
2039 2039 self.register_magics(m.AutoMagics, m.BasicMagics, m.CodeMagics,
2040 2040 m.ConfigMagics, m.DeprecatedMagics, m.DisplayMagics, m.ExecutionMagics,
2041 2041 m.ExtensionMagics, m.HistoryMagics, m.LoggingMagics,
2042 2042 m.NamespaceMagics, m.OSMagics, m.PylabMagics, m.ScriptMagics,
2043 2043 )
2044 2044
2045 2045 # Register Magic Aliases
2046 2046 mman = self.magics_manager
2047 2047 mman.register_alias('ed', 'edit')
2048 2048 mman.register_alias('hist', 'history')
2049 2049 mman.register_alias('rep', 'recall')
2050 2050
2051 2051 # FIXME: Move the color initialization to the DisplayHook, which
2052 2052 # should be split into a prompt manager and displayhook. We probably
2053 2053 # even need a centralize colors management object.
2054 2054 self.magic('colors %s' % self.colors)
2055 2055
2056 2056 def run_line_magic(self, magic_name, line):
2057 2057 """Execute the given line magic.
2058 2058
2059 2059 Parameters
2060 2060 ----------
2061 2061 magic_name : str
2062 2062 Name of the desired magic function, without '%' prefix.
2063 2063
2064 2064 line : str
2065 2065 The rest of the input line as a single string.
2066 2066 """
2067 2067 fn = self.find_line_magic(magic_name)
2068 2068 if fn is None:
2069 2069 cm = self.find_cell_magic(magic_name)
2070 2070 etpl = "Line magic function `%%%s` not found%s."
2071 2071 extra = '' if cm is None else (' (But cell magic `%%%%%s` exists, '
2072 2072 'did you mean that instead?)' % magic_name )
2073 2073 error(etpl % (magic_name, extra))
2074 2074 else:
2075 2075 # Note: this is the distance in the stack to the user's frame.
2076 2076 # This will need to be updated if the internal calling logic gets
2077 2077 # refactored, or else we'll be expanding the wrong variables.
2078 2078 stack_depth = 2
2079 2079 magic_arg_s = self.var_expand(line, stack_depth)
2080 2080 # Put magic args in a list so we can call with f(*a) syntax
2081 2081 args = [magic_arg_s]
2082 2082 kwargs = {}
2083 2083 # Grab local namespace if we need it:
2084 2084 if getattr(fn, "needs_local_scope", False):
2085 2085 kwargs['local_ns'] = sys._getframe(stack_depth).f_locals
2086 2086 with self.builtin_trap:
2087 2087 result = fn(*args,**kwargs)
2088 2088 return result
2089 2089
2090 2090 def run_cell_magic(self, magic_name, line, cell):
2091 2091 """Execute the given cell magic.
2092 2092
2093 2093 Parameters
2094 2094 ----------
2095 2095 magic_name : str
2096 2096 Name of the desired magic function, without '%' prefix.
2097 2097
2098 2098 line : str
2099 2099 The rest of the first input line as a single string.
2100 2100
2101 2101 cell : str
2102 2102 The body of the cell as a (possibly multiline) string.
2103 2103 """
2104 2104 fn = self.find_cell_magic(magic_name)
2105 2105 if fn is None:
2106 2106 lm = self.find_line_magic(magic_name)
2107 2107 etpl = "Cell magic function `%%%%%s` not found%s."
2108 2108 extra = '' if lm is None else (' (But line magic `%%%s` exists, '
2109 2109 'did you mean that instead?)' % magic_name )
2110 2110 error(etpl % (magic_name, extra))
2111 2111 else:
2112 2112 # Note: this is the distance in the stack to the user's frame.
2113 2113 # This will need to be updated if the internal calling logic gets
2114 2114 # refactored, or else we'll be expanding the wrong variables.
2115 2115 stack_depth = 2
2116 2116 magic_arg_s = self.var_expand(line, stack_depth)
2117 2117 with self.builtin_trap:
2118 2118 result = fn(magic_arg_s, cell)
2119 2119 return result
2120 2120
2121 2121 def find_line_magic(self, magic_name):
2122 2122 """Find and return a line magic by name.
2123 2123
2124 2124 Returns None if the magic isn't found."""
2125 2125 return self.magics_manager.magics['line'].get(magic_name)
2126 2126
2127 2127 def find_cell_magic(self, magic_name):
2128 2128 """Find and return a cell magic by name.
2129 2129
2130 2130 Returns None if the magic isn't found."""
2131 2131 return self.magics_manager.magics['cell'].get(magic_name)
2132 2132
2133 2133 def find_magic(self, magic_name, magic_kind='line'):
2134 2134 """Find and return a magic of the given type by name.
2135 2135
2136 2136 Returns None if the magic isn't found."""
2137 2137 return self.magics_manager.magics[magic_kind].get(magic_name)
2138 2138
2139 2139 def magic(self, arg_s):
2140 2140 """DEPRECATED. Use run_line_magic() instead.
2141 2141
2142 2142 Call a magic function by name.
2143 2143
2144 2144 Input: a string containing the name of the magic function to call and
2145 2145 any additional arguments to be passed to the magic.
2146 2146
2147 2147 magic('name -opt foo bar') is equivalent to typing at the ipython
2148 2148 prompt:
2149 2149
2150 2150 In[1]: %name -opt foo bar
2151 2151
2152 2152 To call a magic without arguments, simply use magic('name').
2153 2153
2154 2154 This provides a proper Python function to call IPython's magics in any
2155 2155 valid Python code you can type at the interpreter, including loops and
2156 2156 compound statements.
2157 2157 """
2158 2158 # TODO: should we issue a loud deprecation warning here?
2159 2159 magic_name, _, magic_arg_s = arg_s.partition(' ')
2160 2160 magic_name = magic_name.lstrip(prefilter.ESC_MAGIC)
2161 2161 return self.run_line_magic(magic_name, magic_arg_s)
2162 2162
2163 2163 #-------------------------------------------------------------------------
2164 2164 # Things related to macros
2165 2165 #-------------------------------------------------------------------------
2166 2166
2167 2167 def define_macro(self, name, themacro):
2168 2168 """Define a new macro
2169 2169
2170 2170 Parameters
2171 2171 ----------
2172 2172 name : str
2173 2173 The name of the macro.
2174 2174 themacro : str or Macro
2175 2175 The action to do upon invoking the macro. If a string, a new
2176 2176 Macro object is created by passing the string to it.
2177 2177 """
2178 2178
2179 2179 from IPython.core import macro
2180 2180
2181 2181 if isinstance(themacro, basestring):
2182 2182 themacro = macro.Macro(themacro)
2183 2183 if not isinstance(themacro, macro.Macro):
2184 2184 raise ValueError('A macro must be a string or a Macro instance.')
2185 2185 self.user_ns[name] = themacro
2186 2186
2187 2187 #-------------------------------------------------------------------------
2188 2188 # Things related to the running of system commands
2189 2189 #-------------------------------------------------------------------------
2190 2190
2191 2191 def system_piped(self, cmd):
2192 2192 """Call the given cmd in a subprocess, piping stdout/err
2193 2193
2194 2194 Parameters
2195 2195 ----------
2196 2196 cmd : str
2197 2197 Command to execute (can not end in '&', as background processes are
2198 2198 not supported. Should not be a command that expects input
2199 2199 other than simple text.
2200 2200 """
2201 2201 if cmd.rstrip().endswith('&'):
2202 2202 # this is *far* from a rigorous test
2203 2203 # We do not support backgrounding processes because we either use
2204 2204 # pexpect or pipes to read from. Users can always just call
2205 2205 # os.system() or use ip.system=ip.system_raw
2206 2206 # if they really want a background process.
2207 2207 raise OSError("Background processes not supported.")
2208 2208
2209 2209 # we explicitly do NOT return the subprocess status code, because
2210 2210 # a non-None value would trigger :func:`sys.displayhook` calls.
2211 2211 # Instead, we store the exit_code in user_ns.
2212 2212 self.user_ns['_exit_code'] = system(self.var_expand(cmd, depth=1))
2213 2213
2214 2214 def system_raw(self, cmd):
2215 2215 """Call the given cmd in a subprocess using os.system
2216 2216
2217 2217 Parameters
2218 2218 ----------
2219 2219 cmd : str
2220 2220 Command to execute.
2221 2221 """
2222 2222 cmd = self.var_expand(cmd, depth=1)
2223 2223 # protect os.system from UNC paths on Windows, which it can't handle:
2224 2224 if sys.platform == 'win32':
2225 2225 from IPython.utils._process_win32 import AvoidUNCPath
2226 2226 with AvoidUNCPath() as path:
2227 2227 if path is not None:
2228 2228 cmd = '"pushd %s &&"%s' % (path, cmd)
2229 2229 cmd = py3compat.unicode_to_str(cmd)
2230 2230 ec = os.system(cmd)
2231 2231 else:
2232 2232 cmd = py3compat.unicode_to_str(cmd)
2233 2233 ec = os.system(cmd)
2234 2234
2235 2235 # We explicitly do NOT return the subprocess status code, because
2236 2236 # a non-None value would trigger :func:`sys.displayhook` calls.
2237 2237 # Instead, we store the exit_code in user_ns.
2238 2238 self.user_ns['_exit_code'] = ec
2239 2239
2240 2240 # use piped system by default, because it is better behaved
2241 2241 system = system_piped
2242 2242
2243 2243 def getoutput(self, cmd, split=True, depth=0):
2244 2244 """Get output (possibly including stderr) from a subprocess.
2245 2245
2246 2246 Parameters
2247 2247 ----------
2248 2248 cmd : str
2249 2249 Command to execute (can not end in '&', as background processes are
2250 2250 not supported.
2251 2251 split : bool, optional
2252 2252 If True, split the output into an IPython SList. Otherwise, an
2253 2253 IPython LSString is returned. These are objects similar to normal
2254 2254 lists and strings, with a few convenience attributes for easier
2255 2255 manipulation of line-based output. You can use '?' on them for
2256 2256 details.
2257 2257 depth : int, optional
2258 2258 How many frames above the caller are the local variables which should
2259 2259 be expanded in the command string? The default (0) assumes that the
2260 2260 expansion variables are in the stack frame calling this function.
2261 2261 """
2262 2262 if cmd.rstrip().endswith('&'):
2263 2263 # this is *far* from a rigorous test
2264 2264 raise OSError("Background processes not supported.")
2265 2265 out = getoutput(self.var_expand(cmd, depth=depth+1))
2266 2266 if split:
2267 2267 out = SList(out.splitlines())
2268 2268 else:
2269 2269 out = LSString(out)
2270 2270 return out
2271 2271
2272 2272 #-------------------------------------------------------------------------
2273 2273 # Things related to aliases
2274 2274 #-------------------------------------------------------------------------
2275 2275
2276 2276 def init_alias(self):
2277 2277 self.alias_manager = AliasManager(shell=self, config=self.config)
2278 2278 self.configurables.append(self.alias_manager)
2279 2279 self.ns_table['alias'] = self.alias_manager.alias_table,
2280 2280
2281 2281 #-------------------------------------------------------------------------
2282 2282 # Things related to extensions
2283 2283 #-------------------------------------------------------------------------
2284 2284
2285 2285 def init_extension_manager(self):
2286 2286 self.extension_manager = ExtensionManager(shell=self, config=self.config)
2287 2287 self.configurables.append(self.extension_manager)
2288 2288
2289 2289 #-------------------------------------------------------------------------
2290 2290 # Things related to payloads
2291 2291 #-------------------------------------------------------------------------
2292 2292
2293 2293 def init_payload(self):
2294 2294 self.payload_manager = PayloadManager(config=self.config)
2295 2295 self.configurables.append(self.payload_manager)
2296 2296
2297 2297 #-------------------------------------------------------------------------
2298 2298 # Things related to the prefilter
2299 2299 #-------------------------------------------------------------------------
2300 2300
2301 2301 def init_prefilter(self):
2302 2302 self.prefilter_manager = PrefilterManager(shell=self, config=self.config)
2303 2303 self.configurables.append(self.prefilter_manager)
2304 2304 # Ultimately this will be refactored in the new interpreter code, but
2305 2305 # for now, we should expose the main prefilter method (there's legacy
2306 2306 # code out there that may rely on this).
2307 2307 self.prefilter = self.prefilter_manager.prefilter_lines
2308 2308
2309 2309 def auto_rewrite_input(self, cmd):
2310 2310 """Print to the screen the rewritten form of the user's command.
2311 2311
2312 2312 This shows visual feedback by rewriting input lines that cause
2313 2313 automatic calling to kick in, like::
2314 2314
2315 2315 /f x
2316 2316
2317 2317 into::
2318 2318
2319 2319 ------> f(x)
2320 2320
2321 2321 after the user's input prompt. This helps the user understand that the
2322 2322 input line was transformed automatically by IPython.
2323 2323 """
2324 2324 if not self.show_rewritten_input:
2325 2325 return
2326 2326
2327 2327 rw = self.prompt_manager.render('rewrite') + cmd
2328 2328
2329 2329 try:
2330 2330 # plain ascii works better w/ pyreadline, on some machines, so
2331 2331 # we use it and only print uncolored rewrite if we have unicode
2332 2332 rw = str(rw)
2333 2333 print(rw, file=io.stdout)
2334 2334 except UnicodeEncodeError:
2335 2335 print("------> " + cmd)
2336 2336
2337 2337 #-------------------------------------------------------------------------
2338 2338 # Things related to extracting values/expressions from kernel and user_ns
2339 2339 #-------------------------------------------------------------------------
2340 2340
2341 2341 def _simple_error(self):
2342 2342 etype, value = sys.exc_info()[:2]
2343 2343 return u'[ERROR] {e.__name__}: {v}'.format(e=etype, v=value)
2344 2344
2345 2345 def user_variables(self, names):
2346 2346 """Get a list of variable names from the user's namespace.
2347 2347
2348 2348 Parameters
2349 2349 ----------
2350 2350 names : list of strings
2351 2351 A list of names of variables to be read from the user namespace.
2352 2352
2353 2353 Returns
2354 2354 -------
2355 2355 A dict, keyed by the input names and with the repr() of each value.
2356 2356 """
2357 2357 out = {}
2358 2358 user_ns = self.user_ns
2359 2359 for varname in names:
2360 2360 try:
2361 2361 value = repr(user_ns[varname])
2362 2362 except:
2363 2363 value = self._simple_error()
2364 2364 out[varname] = value
2365 2365 return out
2366 2366
2367 2367 def user_expressions(self, expressions):
2368 2368 """Evaluate a dict of expressions in the user's namespace.
2369 2369
2370 2370 Parameters
2371 2371 ----------
2372 2372 expressions : dict
2373 2373 A dict with string keys and string values. The expression values
2374 2374 should be valid Python expressions, each of which will be evaluated
2375 2375 in the user namespace.
2376 2376
2377 2377 Returns
2378 2378 -------
2379 2379 A dict, keyed like the input expressions dict, with the repr() of each
2380 2380 value.
2381 2381 """
2382 2382 out = {}
2383 2383 user_ns = self.user_ns
2384 2384 global_ns = self.user_global_ns
2385 2385 for key, expr in expressions.iteritems():
2386 2386 try:
2387 2387 value = repr(eval(expr, global_ns, user_ns))
2388 2388 except:
2389 2389 value = self._simple_error()
2390 2390 out[key] = value
2391 2391 return out
2392 2392
2393 2393 #-------------------------------------------------------------------------
2394 2394 # Things related to the running of code
2395 2395 #-------------------------------------------------------------------------
2396 2396
2397 2397 def ex(self, cmd):
2398 2398 """Execute a normal python statement in user namespace."""
2399 2399 with self.builtin_trap:
2400 2400 exec cmd in self.user_global_ns, self.user_ns
2401 2401
2402 2402 def ev(self, expr):
2403 2403 """Evaluate python expression expr in user namespace.
2404 2404
2405 2405 Returns the result of evaluation
2406 2406 """
2407 2407 with self.builtin_trap:
2408 2408 return eval(expr, self.user_global_ns, self.user_ns)
2409 2409
2410 2410 def safe_execfile(self, fname, *where, **kw):
2411 2411 """A safe version of the builtin execfile().
2412 2412
2413 2413 This version will never throw an exception, but instead print
2414 2414 helpful error messages to the screen. This only works on pure
2415 2415 Python files with the .py extension.
2416 2416
2417 2417 Parameters
2418 2418 ----------
2419 2419 fname : string
2420 2420 The name of the file to be executed.
2421 2421 where : tuple
2422 2422 One or two namespaces, passed to execfile() as (globals,locals).
2423 2423 If only one is given, it is passed as both.
2424 2424 exit_ignore : bool (False)
2425 2425 If True, then silence SystemExit for non-zero status (it is always
2426 2426 silenced for zero status, as it is so common).
2427 2427 raise_exceptions : bool (False)
2428 2428 If True raise exceptions everywhere. Meant for testing.
2429 2429
2430 2430 """
2431 2431 kw.setdefault('exit_ignore', False)
2432 2432 kw.setdefault('raise_exceptions', False)
2433 2433
2434 2434 fname = os.path.abspath(os.path.expanduser(fname))
2435 2435
2436 2436 # Make sure we can open the file
2437 2437 try:
2438 2438 with open(fname) as thefile:
2439 2439 pass
2440 2440 except:
2441 2441 warn('Could not open file <%s> for safe execution.' % fname)
2442 2442 return
2443 2443
2444 2444 # Find things also in current directory. This is needed to mimic the
2445 2445 # behavior of running a script from the system command line, where
2446 2446 # Python inserts the script's directory into sys.path
2447 2447 dname = os.path.dirname(fname)
2448 2448
2449 2449 with prepended_to_syspath(dname):
2450 2450 try:
2451 2451 py3compat.execfile(fname,*where)
2452 2452 except SystemExit as status:
2453 2453 # If the call was made with 0 or None exit status (sys.exit(0)
2454 2454 # or sys.exit() ), don't bother showing a traceback, as both of
2455 2455 # these are considered normal by the OS:
2456 2456 # > python -c'import sys;sys.exit(0)'; echo $?
2457 2457 # 0
2458 2458 # > python -c'import sys;sys.exit()'; echo $?
2459 2459 # 0
2460 2460 # For other exit status, we show the exception unless
2461 2461 # explicitly silenced, but only in short form.
2462 2462 if kw['raise_exceptions']:
2463 2463 raise
2464 2464 if status.code not in (0, None) and not kw['exit_ignore']:
2465 2465 self.showtraceback(exception_only=True)
2466 2466 except:
2467 2467 if kw['raise_exceptions']:
2468 2468 raise
2469 2469 self.showtraceback()
2470 2470
2471 2471 def safe_execfile_ipy(self, fname):
2472 2472 """Like safe_execfile, but for .ipy files with IPython syntax.
2473 2473
2474 2474 Parameters
2475 2475 ----------
2476 2476 fname : str
2477 2477 The name of the file to execute. The filename must have a
2478 2478 .ipy extension.
2479 2479 """
2480 2480 fname = os.path.abspath(os.path.expanduser(fname))
2481 2481
2482 2482 # Make sure we can open the file
2483 2483 try:
2484 2484 with open(fname) as thefile:
2485 2485 pass
2486 2486 except:
2487 2487 warn('Could not open file <%s> for safe execution.' % fname)
2488 2488 return
2489 2489
2490 2490 # Find things also in current directory. This is needed to mimic the
2491 2491 # behavior of running a script from the system command line, where
2492 2492 # Python inserts the script's directory into sys.path
2493 2493 dname = os.path.dirname(fname)
2494 2494
2495 2495 with prepended_to_syspath(dname):
2496 2496 try:
2497 2497 with open(fname) as thefile:
2498 2498 # self.run_cell currently captures all exceptions
2499 2499 # raised in user code. It would be nice if there were
2500 2500 # versions of runlines, execfile that did raise, so
2501 2501 # we could catch the errors.
2502 2502 self.run_cell(thefile.read(), store_history=False)
2503 2503 except:
2504 2504 self.showtraceback()
2505 2505 warn('Unknown failure executing file: <%s>' % fname)
2506 2506
2507 2507 def safe_run_module(self, mod_name, where):
2508 2508 """A safe version of runpy.run_module().
2509 2509
2510 2510 This version will never throw an exception, but instead print
2511 2511 helpful error messages to the screen.
2512 2512
2513 2513 Parameters
2514 2514 ----------
2515 2515 mod_name : string
2516 2516 The name of the module to be executed.
2517 2517 where : dict
2518 2518 The globals namespace.
2519 2519 """
2520 2520 try:
2521 2521 where.update(
2522 2522 runpy.run_module(str(mod_name), run_name="__main__",
2523 2523 alter_sys=True)
2524 2524 )
2525 2525 except:
2526 2526 self.showtraceback()
2527 2527 warn('Unknown failure executing module: <%s>' % mod_name)
2528 2528
2529 2529 def _run_cached_cell_magic(self, magic_name, line):
2530 2530 """Special method to call a cell magic with the data stored in self.
2531 2531 """
2532 2532 cell = self._current_cell_magic_body
2533 2533 self._current_cell_magic_body = None
2534 2534 return self.run_cell_magic(magic_name, line, cell)
2535 2535
2536 2536 def run_cell(self, raw_cell, store_history=False, silent=False):
2537 2537 """Run a complete IPython cell.
2538 2538
2539 2539 Parameters
2540 2540 ----------
2541 2541 raw_cell : str
2542 2542 The code (including IPython code such as %magic functions) to run.
2543 2543 store_history : bool
2544 2544 If True, the raw and translated cell will be stored in IPython's
2545 2545 history. For user code calling back into IPython's machinery, this
2546 2546 should be set to False.
2547 2547 silent : bool
2548 2548 If True, avoid side-effects, such as implicit displayhooks and
2549 2549 and logging. silent=True forces store_history=False.
2550 2550 """
2551 2551 if (not raw_cell) or raw_cell.isspace():
2552 2552 return
2553 2553
2554 2554 if silent:
2555 2555 store_history = False
2556 2556
2557 2557 self.input_splitter.push(raw_cell)
2558 2558
2559 2559 # Check for cell magics, which leave state behind. This interface is
2560 2560 # ugly, we need to do something cleaner later... Now the logic is
2561 2561 # simply that the input_splitter remembers if there was a cell magic,
2562 2562 # and in that case we grab the cell body.
2563 2563 if self.input_splitter.cell_magic_parts:
2564 2564 self._current_cell_magic_body = \
2565 2565 ''.join(self.input_splitter.cell_magic_parts)
2566 2566 cell = self.input_splitter.source_reset()
2567 2567
2568 2568 with self.builtin_trap:
2569 2569 prefilter_failed = False
2570 2570 if len(cell.splitlines()) == 1:
2571 2571 try:
2572 2572 # use prefilter_lines to handle trailing newlines
2573 2573 # restore trailing newline for ast.parse
2574 2574 cell = self.prefilter_manager.prefilter_lines(cell) + '\n'
2575 2575 except AliasError as e:
2576 2576 error(e)
2577 2577 prefilter_failed = True
2578 2578 except Exception:
2579 2579 # don't allow prefilter errors to crash IPython
2580 2580 self.showtraceback()
2581 2581 prefilter_failed = True
2582 2582
2583 2583 # Store raw and processed history
2584 2584 if store_history:
2585 2585 self.history_manager.store_inputs(self.execution_count,
2586 2586 cell, raw_cell)
2587 2587 if not silent:
2588 2588 self.logger.log(cell, raw_cell)
2589 2589
2590 2590 if not prefilter_failed:
2591 2591 # don't run if prefilter failed
2592 2592 cell_name = self.compile.cache(cell, self.execution_count)
2593 2593
2594 2594 with self.display_trap:
2595 2595 try:
2596 2596 code_ast = self.compile.ast_parse(cell,
2597 2597 filename=cell_name)
2598 2598 except IndentationError:
2599 2599 self.showindentationerror()
2600 2600 if store_history:
2601 2601 self.execution_count += 1
2602 2602 return None
2603 2603 except (OverflowError, SyntaxError, ValueError, TypeError,
2604 2604 MemoryError):
2605 2605 self.showsyntaxerror()
2606 2606 if store_history:
2607 2607 self.execution_count += 1
2608 2608 return None
2609 2609
2610 2610 interactivity = "none" if silent else self.ast_node_interactivity
2611 2611 self.run_ast_nodes(code_ast.body, cell_name,
2612 2612 interactivity=interactivity)
2613 2613
2614 2614 # Execute any registered post-execution functions.
2615 2615 # unless we are silent
2616 2616 post_exec = [] if silent else self._post_execute.iteritems()
2617 2617
2618 2618 for func, status in post_exec:
2619 2619 if self.disable_failing_post_execute and not status:
2620 2620 continue
2621 2621 try:
2622 2622 func()
2623 2623 except KeyboardInterrupt:
2624 2624 print("\nKeyboardInterrupt", file=io.stderr)
2625 2625 except Exception:
2626 2626 # register as failing:
2627 2627 self._post_execute[func] = False
2628 2628 self.showtraceback()
2629 2629 print('\n'.join([
2630 2630 "post-execution function %r produced an error." % func,
2631 2631 "If this problem persists, you can disable failing post-exec functions with:",
2632 2632 "",
2633 2633 " get_ipython().disable_failing_post_execute = True"
2634 2634 ]), file=io.stderr)
2635 2635
2636 2636 if store_history:
2637 2637 # Write output to the database. Does nothing unless
2638 2638 # history output logging is enabled.
2639 2639 self.history_manager.store_output(self.execution_count)
2640 2640 # Each cell is a *single* input, regardless of how many lines it has
2641 2641 self.execution_count += 1
2642 2642
2643 2643 def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr'):
2644 2644 """Run a sequence of AST nodes. The execution mode depends on the
2645 2645 interactivity parameter.
2646 2646
2647 2647 Parameters
2648 2648 ----------
2649 2649 nodelist : list
2650 2650 A sequence of AST nodes to run.
2651 2651 cell_name : str
2652 2652 Will be passed to the compiler as the filename of the cell. Typically
2653 2653 the value returned by ip.compile.cache(cell).
2654 2654 interactivity : str
2655 2655 'all', 'last', 'last_expr' or 'none', specifying which nodes should be
2656 2656 run interactively (displaying output from expressions). 'last_expr'
2657 2657 will run the last node interactively only if it is an expression (i.e.
2658 2658 expressions in loops or other blocks are not displayed. Other values
2659 2659 for this parameter will raise a ValueError.
2660 2660 """
2661 2661 if not nodelist:
2662 2662 return
2663 2663
2664 2664 if interactivity == 'last_expr':
2665 2665 if isinstance(nodelist[-1], ast.Expr):
2666 2666 interactivity = "last"
2667 2667 else:
2668 2668 interactivity = "none"
2669 2669
2670 2670 if interactivity == 'none':
2671 2671 to_run_exec, to_run_interactive = nodelist, []
2672 2672 elif interactivity == 'last':
2673 2673 to_run_exec, to_run_interactive = nodelist[:-1], nodelist[-1:]
2674 2674 elif interactivity == 'all':
2675 2675 to_run_exec, to_run_interactive = [], nodelist
2676 2676 else:
2677 2677 raise ValueError("Interactivity was %r" % interactivity)
2678 2678
2679 2679 exec_count = self.execution_count
2680 2680
2681 2681 try:
2682 2682 for i, node in enumerate(to_run_exec):
2683 2683 mod = ast.Module([node])
2684 2684 code = self.compile(mod, cell_name, "exec")
2685 2685 if self.run_code(code):
2686 2686 return True
2687 2687
2688 2688 for i, node in enumerate(to_run_interactive):
2689 2689 mod = ast.Interactive([node])
2690 2690 code = self.compile(mod, cell_name, "single")
2691 2691 if self.run_code(code):
2692 2692 return True
2693 2693
2694 2694 # Flush softspace
2695 2695 if softspace(sys.stdout, 0):
2696 2696 print()
2697 2697
2698 2698 except:
2699 2699 # It's possible to have exceptions raised here, typically by
2700 2700 # compilation of odd code (such as a naked 'return' outside a
2701 2701 # function) that did parse but isn't valid. Typically the exception
2702 2702 # is a SyntaxError, but it's safest just to catch anything and show
2703 2703 # the user a traceback.
2704 2704
2705 2705 # We do only one try/except outside the loop to minimize the impact
2706 2706 # on runtime, and also because if any node in the node list is
2707 2707 # broken, we should stop execution completely.
2708 2708 self.showtraceback()
2709 2709
2710 2710 return False
2711 2711
2712 2712 def run_code(self, code_obj):
2713 2713 """Execute a code object.
2714 2714
2715 2715 When an exception occurs, self.showtraceback() is called to display a
2716 2716 traceback.
2717 2717
2718 2718 Parameters
2719 2719 ----------
2720 2720 code_obj : code object
2721 2721 A compiled code object, to be executed
2722 2722
2723 2723 Returns
2724 2724 -------
2725 2725 False : successful execution.
2726 2726 True : an error occurred.
2727 2727 """
2728 2728
2729 2729 # Set our own excepthook in case the user code tries to call it
2730 2730 # directly, so that the IPython crash handler doesn't get triggered
2731 2731 old_excepthook,sys.excepthook = sys.excepthook, self.excepthook
2732 2732
2733 2733 # we save the original sys.excepthook in the instance, in case config
2734 2734 # code (such as magics) needs access to it.
2735 2735 self.sys_excepthook = old_excepthook
2736 2736 outflag = 1 # happens in more places, so it's easier as default
2737 2737 try:
2738 2738 try:
2739 2739 self.hooks.pre_run_code_hook()
2740 2740 #rprint('Running code', repr(code_obj)) # dbg
2741 2741 exec code_obj in self.user_global_ns, self.user_ns
2742 2742 finally:
2743 2743 # Reset our crash handler in place
2744 2744 sys.excepthook = old_excepthook
2745 2745 except SystemExit:
2746 2746 self.showtraceback(exception_only=True)
2747 2747 warn("To exit: use 'exit', 'quit', or Ctrl-D.", level=1)
2748 2748 except self.custom_exceptions:
2749 2749 etype,value,tb = sys.exc_info()
2750 2750 self.CustomTB(etype,value,tb)
2751 2751 except:
2752 2752 self.showtraceback()
2753 2753 else:
2754 2754 outflag = 0
2755 2755 return outflag
2756 2756
2757 2757 # For backwards compatibility
2758 2758 runcode = run_code
2759 2759
2760 2760 #-------------------------------------------------------------------------
2761 2761 # Things related to GUI support and pylab
2762 2762 #-------------------------------------------------------------------------
2763 2763
2764 2764 def enable_gui(self, gui=None):
2765 2765 raise NotImplementedError('Implement enable_gui in a subclass')
2766 2766
2767 2767 def enable_pylab(self, gui=None, import_all=True, welcome_message=False):
2768 2768 """Activate pylab support at runtime.
2769 2769
2770 2770 This turns on support for matplotlib, preloads into the interactive
2771 2771 namespace all of numpy and pylab, and configures IPython to correctly
2772 2772 interact with the GUI event loop. The GUI backend to be used can be
2773 2773 optionally selected with the optional :param:`gui` argument.
2774 2774
2775 2775 Parameters
2776 2776 ----------
2777 2777 gui : optional, string
2778 2778
2779 2779 If given, dictates the choice of matplotlib GUI backend to use
2780 2780 (should be one of IPython's supported backends, 'qt', 'osx', 'tk',
2781 2781 'gtk', 'wx' or 'inline'), otherwise we use the default chosen by
2782 2782 matplotlib (as dictated by the matplotlib build-time options plus the
2783 2783 user's matplotlibrc configuration file). Note that not all backends
2784 2784 make sense in all contexts, for example a terminal ipython can't
2785 2785 display figures inline.
2786 2786 """
2787 2787 from IPython.core.pylabtools import mpl_runner
2788 2788 # We want to prevent the loading of pylab to pollute the user's
2789 2789 # namespace as shown by the %who* magics, so we execute the activation
2790 2790 # code in an empty namespace, and we update *both* user_ns and
2791 2791 # user_ns_hidden with this information.
2792 2792 ns = {}
2793 2793 try:
2794 2794 gui = pylab_activate(ns, gui, import_all, self, welcome_message=welcome_message)
2795 2795 except KeyError:
2796 2796 error("Backend %r not supported" % gui)
2797 2797 return
2798 2798 self.user_ns.update(ns)
2799 2799 self.user_ns_hidden.update(ns)
2800 2800 # Now we must activate the gui pylab wants to use, and fix %run to take
2801 2801 # plot updates into account
2802 2802 self.enable_gui(gui)
2803 2803 self.magics_manager.registry['ExecutionMagics'].default_runner = \
2804 2804 mpl_runner(self.safe_execfile)
2805 2805
2806 2806 #-------------------------------------------------------------------------
2807 2807 # Utilities
2808 2808 #-------------------------------------------------------------------------
2809 2809
2810 2810 def var_expand(self, cmd, depth=0, formatter=DollarFormatter()):
2811 2811 """Expand python variables in a string.
2812 2812
2813 2813 The depth argument indicates how many frames above the caller should
2814 2814 be walked to look for the local namespace where to expand variables.
2815 2815
2816 2816 The global namespace for expansion is always the user's interactive
2817 2817 namespace.
2818 2818 """
2819 2819 ns = self.user_ns.copy()
2820 2820 ns.update(sys._getframe(depth+1).f_locals)
2821 2821 try:
2822 2822 # We have to use .vformat() here, because 'self' is a valid and common
2823 2823 # name, and expanding **ns for .format() would make it collide with
2824 2824 # the 'self' argument of the method.
2825 2825 cmd = formatter.vformat(cmd, args=[], kwargs=ns)
2826 2826 except Exception:
2827 2827 # if formatter couldn't format, just let it go untransformed
2828 2828 pass
2829 2829 return cmd
2830 2830
2831 2831 def mktempfile(self, data=None, prefix='ipython_edit_'):
2832 2832 """Make a new tempfile and return its filename.
2833 2833
2834 2834 This makes a call to tempfile.mktemp, but it registers the created
2835 2835 filename internally so ipython cleans it up at exit time.
2836 2836
2837 2837 Optional inputs:
2838 2838
2839 2839 - data(None): if data is given, it gets written out to the temp file
2840 2840 immediately, and the file is closed again."""
2841 2841
2842 2842 filename = tempfile.mktemp('.py', prefix)
2843 2843 self.tempfiles.append(filename)
2844 2844
2845 2845 if data:
2846 2846 tmp_file = open(filename,'w')
2847 2847 tmp_file.write(data)
2848 2848 tmp_file.close()
2849 2849 return filename
2850 2850
2851 2851 # TODO: This should be removed when Term is refactored.
2852 2852 def write(self,data):
2853 2853 """Write a string to the default output"""
2854 2854 io.stdout.write(data)
2855 2855
2856 2856 # TODO: This should be removed when Term is refactored.
2857 2857 def write_err(self,data):
2858 2858 """Write a string to the default error output"""
2859 2859 io.stderr.write(data)
2860 2860
2861 2861 def ask_yes_no(self, prompt, default=None):
2862 2862 if self.quiet:
2863 2863 return True
2864 2864 return ask_yes_no(prompt,default)
2865 2865
2866 2866 def show_usage(self):
2867 2867 """Show a usage message"""
2868 2868 page.page(IPython.core.usage.interactive_usage)
2869 2869
2870 2870 def extract_input_lines(self, range_str, raw=False):
2871 2871 """Return as a string a set of input history slices.
2872 2872
2873 2873 Parameters
2874 2874 ----------
2875 2875 range_str : string
2876 2876 The set of slices is given as a string, like "~5/6-~4/2 4:8 9",
2877 2877 since this function is for use by magic functions which get their
2878 2878 arguments as strings. The number before the / is the session
2879 2879 number: ~n goes n back from the current session.
2880 2880
2881 2881 Optional Parameters:
2882 2882 - raw(False): by default, the processed input is used. If this is
2883 2883 true, the raw input history is used instead.
2884 2884
2885 2885 Note that slices can be called with two notations:
2886 2886
2887 2887 N:M -> standard python form, means including items N...(M-1).
2888 2888
2889 2889 N-M -> include items N..M (closed endpoint)."""
2890 2890 lines = self.history_manager.get_range_by_str(range_str, raw=raw)
2891 2891 return "\n".join(x for _, _, x in lines)
2892 2892
2893 2893 def find_user_code(self, target, raw=True, py_only=False, skip_encoding_cookie=True):
2894 2894 """Get a code string from history, file, url, or a string or macro.
2895 2895
2896 2896 This is mainly used by magic functions.
2897 2897
2898 2898 Parameters
2899 2899 ----------
2900 2900
2901 2901 target : str
2902 2902
2903 2903 A string specifying code to retrieve. This will be tried respectively
2904 2904 as: ranges of input history (see %history for syntax), url,
2905 2905 correspnding .py file, filename, or an expression evaluating to a
2906 2906 string or Macro in the user namespace.
2907 2907
2908 2908 raw : bool
2909 2909 If true (default), retrieve raw history. Has no effect on the other
2910 2910 retrieval mechanisms.
2911 2911
2912 2912 py_only : bool (default False)
2913 2913 Only try to fetch python code, do not try alternative methods to decode file
2914 2914 if unicode fails.
2915 2915
2916 2916 Returns
2917 2917 -------
2918 2918 A string of code.
2919 2919
2920 2920 ValueError is raised if nothing is found, and TypeError if it evaluates
2921 2921 to an object of another type. In each case, .args[0] is a printable
2922 2922 message.
2923 2923 """
2924 2924 code = self.extract_input_lines(target, raw=raw) # Grab history
2925 2925 if code:
2926 2926 return code
2927 2927 utarget = unquote_filename(target)
2928 2928 try:
2929 2929 if utarget.startswith(('http://', 'https://')):
2930 2930 return openpy.read_py_url(utarget, skip_encoding_cookie=skip_encoding_cookie)
2931 2931 except UnicodeDecodeError:
2932 2932 if not py_only :
2933 2933 response = urllib.urlopen(target)
2934 2934 return response.read().decode('latin1')
2935 2935 raise ValueError(("'%s' seem to be unreadable.") % utarget)
2936 2936
2937 2937 potential_target = [target]
2938 2938 try :
2939 2939 potential_target.insert(0,get_py_filename(target))
2940 2940 except IOError:
2941 2941 pass
2942 2942
2943 2943 for tgt in potential_target :
2944 2944 if os.path.isfile(tgt): # Read file
2945 2945 try :
2946 2946 return openpy.read_py_file(tgt, skip_encoding_cookie=skip_encoding_cookie)
2947 2947 except UnicodeDecodeError :
2948 2948 if not py_only :
2949 2949 with io_open(tgt,'r', encoding='latin1') as f :
2950 2950 return f.read()
2951 2951 raise ValueError(("'%s' seem to be unreadable.") % target)
2952 2952
2953 2953 try: # User namespace
2954 2954 codeobj = eval(target, self.user_ns)
2955 2955 except Exception:
2956 2956 raise ValueError(("'%s' was not found in history, as a file, url, "
2957 2957 "nor in the user namespace.") % target)
2958 2958 if isinstance(codeobj, basestring):
2959 2959 return codeobj
2960 2960 elif isinstance(codeobj, Macro):
2961 2961 return codeobj.value
2962 2962
2963 2963 raise TypeError("%s is neither a string nor a macro." % target,
2964 2964 codeobj)
2965 2965
2966 2966 #-------------------------------------------------------------------------
2967 2967 # Things related to IPython exiting
2968 2968 #-------------------------------------------------------------------------
2969 2969 def atexit_operations(self):
2970 2970 """This will be executed at the time of exit.
2971 2971
2972 2972 Cleanup operations and saving of persistent data that is done
2973 2973 unconditionally by IPython should be performed here.
2974 2974
2975 2975 For things that may depend on startup flags or platform specifics (such
2976 2976 as having readline or not), register a separate atexit function in the
2977 2977 code that has the appropriate information, rather than trying to
2978 2978 clutter
2979 2979 """
2980 2980 # Close the history session (this stores the end time and line count)
2981 2981 # this must be *before* the tempfile cleanup, in case of temporary
2982 2982 # history db
2983 2983 self.history_manager.end_session()
2984 2984
2985 2985 # Cleanup all tempfiles left around
2986 2986 for tfile in self.tempfiles:
2987 2987 try:
2988 2988 os.unlink(tfile)
2989 2989 except OSError:
2990 2990 pass
2991 2991
2992 2992 # Clear all user namespaces to release all references cleanly.
2993 2993 self.reset(new_session=False)
2994 2994
2995 2995 # Run user hooks
2996 2996 self.hooks.shutdown_hook()
2997 2997
2998 2998 def cleanup(self):
2999 2999 self.restore_sys_module_state()
3000 3000
3001 3001
3002 3002 class InteractiveShellABC(object):
3003 3003 """An abstract base class for InteractiveShell."""
3004 3004 __metaclass__ = abc.ABCMeta
3005 3005
3006 3006 InteractiveShellABC.register(InteractiveShell)
@@ -1,222 +1,241 b''
1 1 ''' A decorator-based method of constructing IPython magics with `argparse`
2 2 option handling.
3 3
4 4 New magic functions can be defined like so::
5 5
6 6 from IPython.core.magic_arguments import (argument, magic_arguments,
7 7 parse_argstring)
8 8
9 9 @magic_arguments()
10 10 @argument('-o', '--option', help='An optional argument.')
11 11 @argument('arg', type=int, help='An integer positional argument.')
12 12 def magic_cool(self, arg):
13 13 """ A really cool magic command.
14 14
15 15 """
16 16 args = parse_argstring(magic_cool, arg)
17 17 ...
18 18
19 19 The `@magic_arguments` decorator marks the function as having argparse arguments.
20 20 The `@argument` decorator adds an argument using the same syntax as argparse's
21 21 `add_argument()` method. More sophisticated uses may also require the
22 22 `@argument_group` or `@kwds` decorator to customize the formatting and the
23 23 parsing.
24 24
25 25 Help text for the magic is automatically generated from the docstring and the
26 26 arguments::
27 27
28 28 In[1]: %cool?
29 29 %cool [-o OPTION] arg
30 30
31 31 A really cool magic command.
32 32
33 33 positional arguments:
34 34 arg An integer positional argument.
35 35
36 36 optional arguments:
37 37 -o OPTION, --option OPTION
38 38 An optional argument.
39 39
40 40 '''
41 41 #-----------------------------------------------------------------------------
42 42 # Copyright (C) 2010-2011, IPython Development Team.
43 43 #
44 44 # Distributed under the terms of the Modified BSD License.
45 45 #
46 46 # The full license is in the file COPYING.txt, distributed with this software.
47 47 #-----------------------------------------------------------------------------
48 48
49 49 # Our own imports
50 50 from IPython.external import argparse
51 51 from IPython.core.error import UsageError
52 52 from IPython.utils.process import arg_split
53 53 from IPython.utils.text import dedent
54 54
55 55 class MagicHelpFormatter(argparse.RawDescriptionHelpFormatter):
56 56 """ A HelpFormatter which dedents but otherwise preserves indentation.
57 57 """
58 58 def _fill_text(self, text, width, indent):
59 59 return argparse.RawDescriptionHelpFormatter._fill_text(self, dedent(text), width, indent)
60 60
61 61 class MagicArgumentParser(argparse.ArgumentParser):
62 62 """ An ArgumentParser tweaked for use by IPython magics.
63 63 """
64 64 def __init__(self,
65 65 prog=None,
66 66 usage=None,
67 67 description=None,
68 68 epilog=None,
69 69 parents=None,
70 70 formatter_class=MagicHelpFormatter,
71 71 prefix_chars='-',
72 72 argument_default=None,
73 73 conflict_handler='error',
74 74 add_help=False):
75 75 if parents is None:
76 76 parents = []
77 77 super(MagicArgumentParser, self).__init__(prog=prog, usage=usage,
78 78 description=description, epilog=epilog,
79 79 parents=parents, formatter_class=formatter_class,
80 80 prefix_chars=prefix_chars, argument_default=argument_default,
81 81 conflict_handler=conflict_handler, add_help=add_help)
82 82
83 83 def error(self, message):
84 84 """ Raise a catchable error instead of exiting.
85 85 """
86 86 raise UsageError(message)
87 87
88 88 def parse_argstring(self, argstring):
89 89 """ Split a string into an argument list and parse that argument list.
90 90 """
91 91 argv = arg_split(argstring)
92 92 return self.parse_args(argv)
93 93
94 94
95 95 def construct_parser(magic_func):
96 96 """ Construct an argument parser using the function decorations.
97 97 """
98 98 kwds = getattr(magic_func, 'argcmd_kwds', {})
99 99 if 'description' not in kwds:
100 100 kwds['description'] = getattr(magic_func, '__doc__', None)
101 101 arg_name = real_name(magic_func)
102 102 parser = MagicArgumentParser(arg_name, **kwds)
103 103 # Reverse the list of decorators in order to apply them in the
104 104 # order in which they appear in the source.
105 105 group = None
106 106 for deco in magic_func.decorators[::-1]:
107 107 result = deco.add_to_parser(parser, group)
108 108 if result is not None:
109 109 group = result
110 110
111 111 # Replace the starting 'usage: ' with IPython's %.
112 112 help_text = parser.format_help()
113 113 if help_text.startswith('usage: '):
114 114 help_text = help_text.replace('usage: ', '%', 1)
115 115 else:
116 116 help_text = '%' + help_text
117 117
118 118 # Replace the magic function's docstring with the full help text.
119 119 magic_func.__doc__ = help_text
120 120
121 121 return parser
122 122
123 123
124 124 def parse_argstring(magic_func, argstring):
125 125 """ Parse the string of arguments for the given magic function.
126 126 """
127 127 return magic_func.parser.parse_argstring(argstring)
128 128
129 129
130 130 def real_name(magic_func):
131 131 """ Find the real name of the magic.
132 132 """
133 133 magic_name = magic_func.__name__
134 134 if magic_name.startswith('magic_'):
135 135 magic_name = magic_name[len('magic_'):]
136 136 return getattr(magic_func, 'argcmd_name', magic_name)
137 137
138 138
139 139 class ArgDecorator(object):
140 140 """ Base class for decorators to add ArgumentParser information to a method.
141 141 """
142 142
143 143 def __call__(self, func):
144 144 if not getattr(func, 'has_arguments', False):
145 145 func.has_arguments = True
146 146 func.decorators = []
147 147 func.decorators.append(self)
148 148 return func
149 149
150 150 def add_to_parser(self, parser, group):
151 151 """ Add this object's information to the parser, if necessary.
152 152 """
153 153 pass
154 154
155 155
156 156 class magic_arguments(ArgDecorator):
157 157 """ Mark the magic as having argparse arguments and possibly adjust the
158 158 name.
159 159 """
160 160
161 161 def __init__(self, name=None):
162 162 self.name = name
163 163
164 164 def __call__(self, func):
165 165 if not getattr(func, 'has_arguments', False):
166 166 func.has_arguments = True
167 167 func.decorators = []
168 168 if self.name is not None:
169 169 func.argcmd_name = self.name
170 170 # This should be the first decorator in the list of decorators, thus the
171 171 # last to execute. Build the parser.
172 172 func.parser = construct_parser(func)
173 173 return func
174 174
175 175
176 class argument(ArgDecorator):
177 """ Store arguments and keywords to pass to add_argument().
176 class ArgMethodWrapper(ArgDecorator):
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 187 def __init__(self, *args, **kwds):
182 188 self.args = args
183 189 self.kwds = kwds
184 190
185 191 def add_to_parser(self, parser, group):
186 192 """ Add this object's information to the parser.
187 193 """
188 194 if group is not None:
189 195 parser = group
190 parser.add_argument(*self.args, **self.kwds)
196 getattr(parser, self._method_name)(*self.args, **self.kwds)
191 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 217 """ Store arguments and keywords to pass to add_argument_group().
196 218
197 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 222 def add_to_parser(self, parser, group):
204 223 """ Add this object's information to the parser.
205 224 """
206 225 return parser.add_argument_group(*self.args, **self.kwds)
207 226
208 227
209 228 class kwds(ArgDecorator):
210 229 """ Provide other keywords to the sub-parser constructor.
211 230 """
212 231 def __init__(self, **kwds):
213 232 self.kwds = kwds
214 233
215 234 def __call__(self, func):
216 235 func = super(kwds, self).__call__(func)
217 236 func.argcmd_kwds = self.kwds
218 237 return func
219 238
220 239
221 240 __all__ = ['magic_arguments', 'argument', 'argument_group', 'kwds',
222 241 'parse_argstring']
@@ -1,612 +1,613 b''
1 1 """Implementation of basic magic functions.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2012 The IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14 from __future__ import print_function
15 15
16 16 # Stdlib
17 17 import io
18 18 import sys
19 19 from pprint import pformat
20 20
21 21 # Our own packages
22 22 from IPython.core import magic_arguments
23 23 from IPython.core.error import UsageError
24 24 from IPython.core.magic import Magics, magics_class, line_magic, magic_escapes
25 25 from IPython.utils.text import format_screen, dedent, indent
26 26 from IPython.core import magic_arguments, page
27 27 from IPython.testing.skipdoctest import skip_doctest
28 28 from IPython.utils.ipstruct import Struct
29 29 from IPython.utils.path import unquote_filename
30 30 from IPython.utils.warn import warn, error
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # Magics class implementation
34 34 #-----------------------------------------------------------------------------
35 35
36 36 @magics_class
37 37 class BasicMagics(Magics):
38 38 """Magics that provide central IPython functionality.
39 39
40 40 These are various magics that don't fit into specific categories but that
41 41 are all part of the base 'IPython experience'."""
42 42
43 43 @magic_arguments.magic_arguments()
44 44 @magic_arguments.argument(
45 45 '-l', '--line', action='store_true',
46 46 help="""Create a line magic alias."""
47 47 )
48 48 @magic_arguments.argument(
49 49 '-c', '--cell', action='store_true',
50 50 help="""Create a cell magic alias."""
51 51 )
52 52 @magic_arguments.argument(
53 53 'name',
54 54 help="""Name of the magic to be created."""
55 55 )
56 56 @magic_arguments.argument(
57 57 'target',
58 58 help="""Name of the existing line or cell magic."""
59 59 )
60 60 @line_magic
61 61 def alias_magic(self, line=''):
62 62 """Create an alias for an existing line or cell magic.
63 63
64 64 Examples
65 65 --------
66 66 ::
67 67 In [1]: %alias_magic t timeit
68 68 Created `%t` as an alias for `%timeit`.
69 69 Created `%%t` as an alias for `%%timeit`.
70 70
71 71 In [2]: %t -n1 pass
72 72 1 loops, best of 3: 954 ns per loop
73 73
74 74 In [3]: %%t -n1
75 75 ...: pass
76 76 ...:
77 77 1 loops, best of 3: 954 ns per loop
78 78
79 79 In [4]: %alias_magic --cell whereami pwd
80 80 UsageError: Cell magic function `%%pwd` not found.
81 81 In [5]: %alias_magic --line whereami pwd
82 82 Created `%whereami` as an alias for `%pwd`.
83 83
84 84 In [6]: %whereami
85 85 Out[6]: u'/home/testuser'
86 86 """
87 87 args = magic_arguments.parse_argstring(self.alias_magic, line)
88 88 shell = self.shell
89 89 mman = self.shell.magics_manager
90 90 escs = ''.join(magic_escapes.values())
91 91
92 92 target = args.target.lstrip(escs)
93 93 name = args.name.lstrip(escs)
94 94
95 95 # Find the requested magics.
96 96 m_line = shell.find_magic(target, 'line')
97 97 m_cell = shell.find_magic(target, 'cell')
98 98 if args.line and m_line is None:
99 99 raise UsageError('Line magic function `%s%s` not found.' %
100 100 (magic_escapes['line'], target))
101 101 if args.cell and m_cell is None:
102 102 raise UsageError('Cell magic function `%s%s` not found.' %
103 103 (magic_escapes['cell'], target))
104 104
105 105 # If --line and --cell are not specified, default to the ones
106 106 # that are available.
107 107 if not args.line and not args.cell:
108 108 if not m_line and not m_cell:
109 109 raise UsageError(
110 110 'No line or cell magic with name `%s` found.' % target
111 111 )
112 112 args.line = bool(m_line)
113 113 args.cell = bool(m_cell)
114 114
115 115 if args.line:
116 116 mman.register_alias(name, target, 'line')
117 117 print('Created `%s%s` as an alias for `%s%s`.' % (
118 118 magic_escapes['line'], name,
119 119 magic_escapes['line'], target))
120 120
121 121 if args.cell:
122 122 mman.register_alias(name, target, 'cell')
123 123 print('Created `%s%s` as an alias for `%s%s`.' % (
124 124 magic_escapes['cell'], name,
125 125 magic_escapes['cell'], target))
126 126
127 127 def _lsmagic(self):
128 128 mesc = magic_escapes['line']
129 129 cesc = magic_escapes['cell']
130 130 mman = self.shell.magics_manager
131 131 magics = mman.lsmagic()
132 132 out = ['Available line magics:',
133 133 mesc + (' '+mesc).join(sorted(magics['line'])),
134 134 '',
135 135 'Available cell magics:',
136 136 cesc + (' '+cesc).join(sorted(magics['cell'])),
137 137 '',
138 138 mman.auto_status()]
139 139 return '\n'.join(out)
140 140
141 141 @line_magic
142 142 def lsmagic(self, parameter_s=''):
143 143 """List currently available magic functions."""
144 144 print(self._lsmagic())
145 145
146 146 def _magic_docs(self, brief=False, rest=False):
147 147 """Return docstrings from magic functions."""
148 148 mman = self.shell.magics_manager
149 149 docs = mman.lsmagic_docs(brief, missing='No documentation')
150 150
151 151 if rest:
152 152 format_string = '**%s%s**::\n\n%s\n\n'
153 153 else:
154 154 format_string = '%s%s:\n%s\n'
155 155
156 156 return ''.join(
157 157 [format_string % (magic_escapes['line'], fname,
158 158 indent(dedent(fndoc)))
159 159 for fname, fndoc in sorted(docs['line'].items())]
160 160 +
161 161 [format_string % (magic_escapes['cell'], fname,
162 162 indent(dedent(fndoc)))
163 163 for fname, fndoc in sorted(docs['cell'].items())]
164 164 )
165 165
166 166 @line_magic
167 167 def magic(self, parameter_s=''):
168 168 """Print information about the magic function system.
169 169
170 170 Supported formats: -latex, -brief, -rest
171 171 """
172 172
173 173 mode = ''
174 174 try:
175 175 mode = parameter_s.split()[0][1:]
176 176 if mode == 'rest':
177 177 rest_docs = []
178 178 except IndexError:
179 179 pass
180 180
181 181 brief = (mode == 'brief')
182 182 rest = (mode == 'rest')
183 183 magic_docs = self._magic_docs(brief, rest)
184 184
185 185 if mode == 'latex':
186 186 print(self.format_latex(magic_docs))
187 187 return
188 188 else:
189 189 magic_docs = format_screen(magic_docs)
190 190
191 191 out = ["""
192 192 IPython's 'magic' functions
193 193 ===========================
194 194
195 195 The magic function system provides a series of functions which allow you to
196 196 control the behavior of IPython itself, plus a lot of system-type
197 197 features. There are two kinds of magics, line-oriented and cell-oriented.
198 198
199 199 Line magics are prefixed with the % character and work much like OS
200 200 command-line calls: they get as an argument the rest of the line, where
201 201 arguments are passed without parentheses or quotes. For example, this will
202 202 time the given statement::
203 203
204 204 %timeit range(1000)
205 205
206 206 Cell magics are prefixed with a double %%, and they are functions that get as
207 207 an argument not only the rest of the line, but also the lines below it in a
208 208 separate argument. These magics are called with two arguments: the rest of the
209 209 call line and the body of the cell, consisting of the lines below the first.
210 210 For example::
211 211
212 212 %%timeit x = numpy.random.randn((100, 100))
213 213 numpy.linalg.svd(x)
214 214
215 215 will time the execution of the numpy svd routine, running the assignment of x
216 216 as part of the setup phase, which is not timed.
217 217
218 218 In a line-oriented client (the terminal or Qt console IPython), starting a new
219 219 input with %% will automatically enter cell mode, and IPython will continue
220 220 reading input until a blank line is given. In the notebook, simply type the
221 221 whole cell as one entity, but keep in mind that the %% escape can only be at
222 222 the very start of the cell.
223 223
224 224 NOTE: If you have 'automagic' enabled (via the command line option or with the
225 225 %automagic function), you don't need to type in the % explicitly for line
226 226 magics; cell magics always require an explicit '%%' escape. By default,
227 227 IPython ships with automagic on, so you should only rarely need the % escape.
228 228
229 229 Example: typing '%cd mydir' (without the quotes) changes you working directory
230 230 to 'mydir', if it exists.
231 231
232 232 For a list of the available magic functions, use %lsmagic. For a description
233 233 of any of them, type %magic_name?, e.g. '%cd?'.
234 234
235 235 Currently the magic system has the following functions:""",
236 236 magic_docs,
237 237 "Summary of magic functions (from %slsmagic):" % magic_escapes['line'],
238 238 self._lsmagic(),
239 239 ]
240 240 page.page('\n'.join(out))
241 241
242 242
243 243 @line_magic
244 244 def page(self, parameter_s=''):
245 245 """Pretty print the object and display it through a pager.
246 246
247 247 %page [options] OBJECT
248 248
249 249 If no object is given, use _ (last output).
250 250
251 251 Options:
252 252
253 253 -r: page str(object), don't pretty-print it."""
254 254
255 255 # After a function contributed by Olivier Aubert, slightly modified.
256 256
257 257 # Process options/args
258 258 opts, args = self.parse_options(parameter_s, 'r')
259 259 raw = 'r' in opts
260 260
261 261 oname = args and args or '_'
262 262 info = self.shell._ofind(oname)
263 263 if info['found']:
264 264 txt = (raw and str or pformat)( info['obj'] )
265 265 page.page(txt)
266 266 else:
267 267 print('Object `%s` not found' % oname)
268 268
269 269 @line_magic
270 270 def profile(self, parameter_s=''):
271 271 """Print your currently active IPython profile."""
272 272 from IPython.core.application import BaseIPythonApplication
273 273 if BaseIPythonApplication.initialized():
274 274 print(BaseIPythonApplication.instance().profile)
275 275 else:
276 276 error("profile is an application-level value, but you don't appear to be in an IPython application")
277 277
278 278 @line_magic
279 279 def pprint(self, parameter_s=''):
280 280 """Toggle pretty printing on/off."""
281 281 ptformatter = self.shell.display_formatter.formatters['text/plain']
282 282 ptformatter.pprint = bool(1 - ptformatter.pprint)
283 283 print('Pretty printing has been turned',
284 284 ['OFF','ON'][ptformatter.pprint])
285 285
286 286 @line_magic
287 287 def colors(self, parameter_s=''):
288 288 """Switch color scheme for prompts, info system and exception handlers.
289 289
290 290 Currently implemented schemes: NoColor, Linux, LightBG.
291 291
292 292 Color scheme names are not case-sensitive.
293 293
294 294 Examples
295 295 --------
296 296 To get a plain black and white terminal::
297 297
298 298 %colors nocolor
299 299 """
300 300 def color_switch_err(name):
301 301 warn('Error changing %s color schemes.\n%s' %
302 302 (name, sys.exc_info()[1]))
303 303
304 304
305 305 new_scheme = parameter_s.strip()
306 306 if not new_scheme:
307 307 raise UsageError(
308 308 "%colors: you must specify a color scheme. See '%colors?'")
309 309 return
310 310 # local shortcut
311 311 shell = self.shell
312 312
313 313 import IPython.utils.rlineimpl as readline
314 314
315 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 318 msg = """\
318 319 Proper color support under MS Windows requires the pyreadline library.
319 320 You can find it at:
320 321 http://ipython.org/pyreadline.html
321 322 Gary's readline needs the ctypes module, from:
322 323 http://starship.python.net/crew/theller/ctypes
323 324 (Note that ctypes is already part of Python versions 2.5 and newer).
324 325
325 326 Defaulting color scheme to 'NoColor'"""
326 327 new_scheme = 'NoColor'
327 328 warn(msg)
328 329
329 330 # readline option is 0
330 331 if not shell.colors_force and not shell.has_readline:
331 332 new_scheme = 'NoColor'
332 333
333 334 # Set prompt colors
334 335 try:
335 336 shell.prompt_manager.color_scheme = new_scheme
336 337 except:
337 338 color_switch_err('prompt')
338 339 else:
339 340 shell.colors = \
340 341 shell.prompt_manager.color_scheme_table.active_scheme_name
341 342 # Set exception colors
342 343 try:
343 344 shell.InteractiveTB.set_colors(scheme = new_scheme)
344 345 shell.SyntaxTB.set_colors(scheme = new_scheme)
345 346 except:
346 347 color_switch_err('exception')
347 348
348 349 # Set info (for 'object?') colors
349 350 if shell.color_info:
350 351 try:
351 352 shell.inspector.set_active_scheme(new_scheme)
352 353 except:
353 354 color_switch_err('object inspector')
354 355 else:
355 356 shell.inspector.set_active_scheme('NoColor')
356 357
357 358 @line_magic
358 359 def xmode(self, parameter_s=''):
359 360 """Switch modes for the exception handlers.
360 361
361 362 Valid modes: Plain, Context and Verbose.
362 363
363 364 If called without arguments, acts as a toggle."""
364 365
365 366 def xmode_switch_err(name):
366 367 warn('Error changing %s exception modes.\n%s' %
367 368 (name,sys.exc_info()[1]))
368 369
369 370 shell = self.shell
370 371 new_mode = parameter_s.strip().capitalize()
371 372 try:
372 373 shell.InteractiveTB.set_mode(mode=new_mode)
373 374 print('Exception reporting mode:',shell.InteractiveTB.mode)
374 375 except:
375 376 xmode_switch_err('user')
376 377
377 378 @line_magic
378 379 def quickref(self,arg):
379 380 """ Show a quick reference sheet """
380 381 from IPython.core.usage import quick_reference
381 382 qr = quick_reference + self._magic_docs(brief=True)
382 383 page.page(qr)
383 384
384 385 @line_magic
385 386 def doctest_mode(self, parameter_s=''):
386 387 """Toggle doctest mode on and off.
387 388
388 389 This mode is intended to make IPython behave as much as possible like a
389 390 plain Python shell, from the perspective of how its prompts, exceptions
390 391 and output look. This makes it easy to copy and paste parts of a
391 392 session into doctests. It does so by:
392 393
393 394 - Changing the prompts to the classic ``>>>`` ones.
394 395 - Changing the exception reporting mode to 'Plain'.
395 396 - Disabling pretty-printing of output.
396 397
397 398 Note that IPython also supports the pasting of code snippets that have
398 399 leading '>>>' and '...' prompts in them. This means that you can paste
399 400 doctests from files or docstrings (even if they have leading
400 401 whitespace), and the code will execute correctly. You can then use
401 402 '%history -t' to see the translated history; this will give you the
402 403 input after removal of all the leading prompts and whitespace, which
403 404 can be pasted back into an editor.
404 405
405 406 With these features, you can switch into this mode easily whenever you
406 407 need to do testing and changes to doctests, without having to leave
407 408 your existing IPython session.
408 409 """
409 410
410 411 # Shorthands
411 412 shell = self.shell
412 413 pm = shell.prompt_manager
413 414 meta = shell.meta
414 415 disp_formatter = self.shell.display_formatter
415 416 ptformatter = disp_formatter.formatters['text/plain']
416 417 # dstore is a data store kept in the instance metadata bag to track any
417 418 # changes we make, so we can undo them later.
418 419 dstore = meta.setdefault('doctest_mode',Struct())
419 420 save_dstore = dstore.setdefault
420 421
421 422 # save a few values we'll need to recover later
422 423 mode = save_dstore('mode',False)
423 424 save_dstore('rc_pprint',ptformatter.pprint)
424 425 save_dstore('xmode',shell.InteractiveTB.mode)
425 426 save_dstore('rc_separate_out',shell.separate_out)
426 427 save_dstore('rc_separate_out2',shell.separate_out2)
427 428 save_dstore('rc_prompts_pad_left',pm.justify)
428 429 save_dstore('rc_separate_in',shell.separate_in)
429 430 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
430 431 save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template))
431 432
432 433 if mode == False:
433 434 # turn on
434 435 pm.in_template = '>>> '
435 436 pm.in2_template = '... '
436 437 pm.out_template = ''
437 438
438 439 # Prompt separators like plain python
439 440 shell.separate_in = ''
440 441 shell.separate_out = ''
441 442 shell.separate_out2 = ''
442 443
443 444 pm.justify = False
444 445
445 446 ptformatter.pprint = False
446 447 disp_formatter.plain_text_only = True
447 448
448 449 shell.magic('xmode Plain')
449 450 else:
450 451 # turn off
451 452 pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates
452 453
453 454 shell.separate_in = dstore.rc_separate_in
454 455
455 456 shell.separate_out = dstore.rc_separate_out
456 457 shell.separate_out2 = dstore.rc_separate_out2
457 458
458 459 pm.justify = dstore.rc_prompts_pad_left
459 460
460 461 ptformatter.pprint = dstore.rc_pprint
461 462 disp_formatter.plain_text_only = dstore.rc_plain_text_only
462 463
463 464 shell.magic('xmode ' + dstore.xmode)
464 465
465 466 # Store new mode and inform
466 467 dstore.mode = bool(1-int(mode))
467 468 mode_label = ['OFF','ON'][dstore.mode]
468 469 print('Doctest mode is:', mode_label)
469 470
470 471 @line_magic
471 472 def gui(self, parameter_s=''):
472 473 """Enable or disable IPython GUI event loop integration.
473 474
474 475 %gui [GUINAME]
475 476
476 477 This magic replaces IPython's threaded shells that were activated
477 478 using the (pylab/wthread/etc.) command line flags. GUI toolkits
478 479 can now be enabled at runtime and keyboard
479 480 interrupts should work without any problems. The following toolkits
480 481 are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX)::
481 482
482 483 %gui wx # enable wxPython event loop integration
483 484 %gui qt4|qt # enable PyQt4 event loop integration
484 485 %gui gtk # enable PyGTK event loop integration
485 486 %gui gtk3 # enable Gtk3 event loop integration
486 487 %gui tk # enable Tk event loop integration
487 488 %gui osx # enable Cocoa event loop integration
488 489 # (requires %matplotlib 1.1)
489 490 %gui # disable all event loop integration
490 491
491 492 WARNING: after any of these has been called you can simply create
492 493 an application object, but DO NOT start the event loop yourself, as
493 494 we have already handled that.
494 495 """
495 496 opts, arg = self.parse_options(parameter_s, '')
496 497 if arg=='': arg = None
497 498 try:
498 499 return self.shell.enable_gui(arg)
499 500 except Exception as e:
500 501 # print simple error message, rather than traceback if we can't
501 502 # hook up the GUI
502 503 error(str(e))
503 504
504 505 @skip_doctest
505 506 @line_magic
506 507 def precision(self, s=''):
507 508 """Set floating point precision for pretty printing.
508 509
509 510 Can set either integer precision or a format string.
510 511
511 512 If numpy has been imported and precision is an int,
512 513 numpy display precision will also be set, via ``numpy.set_printoptions``.
513 514
514 515 If no argument is given, defaults will be restored.
515 516
516 517 Examples
517 518 --------
518 519 ::
519 520
520 521 In [1]: from math import pi
521 522
522 523 In [2]: %precision 3
523 524 Out[2]: u'%.3f'
524 525
525 526 In [3]: pi
526 527 Out[3]: 3.142
527 528
528 529 In [4]: %precision %i
529 530 Out[4]: u'%i'
530 531
531 532 In [5]: pi
532 533 Out[5]: 3
533 534
534 535 In [6]: %precision %e
535 536 Out[6]: u'%e'
536 537
537 538 In [7]: pi**10
538 539 Out[7]: 9.364805e+04
539 540
540 541 In [8]: %precision
541 542 Out[8]: u'%r'
542 543
543 544 In [9]: pi**10
544 545 Out[9]: 93648.047476082982
545 546 """
546 547 ptformatter = self.shell.display_formatter.formatters['text/plain']
547 548 ptformatter.float_precision = s
548 549 return ptformatter.float_format
549 550
550 551 @magic_arguments.magic_arguments()
551 552 @magic_arguments.argument(
552 553 '-e', '--export', action='store_true', default=False,
553 554 help='Export IPython history as a notebook. The filename argument '
554 555 'is used to specify the notebook name and format. For example '
555 556 'a filename of notebook.ipynb will result in a notebook name '
556 557 'of "notebook" and a format of "xml". Likewise using a ".json" '
557 558 'or ".py" file extension will write the notebook in the json '
558 559 'or py formats.'
559 560 )
560 561 @magic_arguments.argument(
561 562 '-f', '--format',
562 563 help='Convert an existing IPython notebook to a new format. This option '
563 564 'specifies the new format and can have the values: xml, json, py. '
564 565 'The target filename is chosen automatically based on the new '
565 566 'format. The filename argument gives the name of the source file.'
566 567 )
567 568 @magic_arguments.argument(
568 569 'filename', type=unicode,
569 570 help='Notebook name or filename'
570 571 )
571 572 @line_magic
572 573 def notebook(self, s):
573 574 """Export and convert IPython notebooks.
574 575
575 576 This function can export the current IPython history to a notebook file
576 577 or can convert an existing notebook file into a different format. For
577 578 example, to export the history to "foo.ipynb" do "%notebook -e foo.ipynb".
578 579 To export the history to "foo.py" do "%notebook -e foo.py". To convert
579 580 "foo.ipynb" to "foo.json" do "%notebook -f json foo.ipynb". Possible
580 581 formats include (json/ipynb, py).
581 582 """
582 583 args = magic_arguments.parse_argstring(self.notebook, s)
583 584
584 585 from IPython.nbformat import current
585 586 args.filename = unquote_filename(args.filename)
586 587 if args.export:
587 588 fname, name, format = current.parse_filename(args.filename)
588 589 cells = []
589 590 hist = list(self.shell.history_manager.get_range())
590 591 for session, prompt_number, input in hist[:-1]:
591 592 cells.append(current.new_code_cell(prompt_number=prompt_number,
592 593 input=input))
593 594 worksheet = current.new_worksheet(cells=cells)
594 595 nb = current.new_notebook(name=name,worksheets=[worksheet])
595 596 with io.open(fname, 'w', encoding='utf-8') as f:
596 597 current.write(nb, f, format);
597 598 elif args.format is not None:
598 599 old_fname, old_name, old_format = current.parse_filename(args.filename)
599 600 new_format = args.format
600 601 if new_format == u'xml':
601 602 raise ValueError('Notebooks cannot be written as xml.')
602 603 elif new_format == u'ipynb' or new_format == u'json':
603 604 new_fname = old_name + u'.ipynb'
604 605 new_format = u'json'
605 606 elif new_format == u'py':
606 607 new_fname = old_name + u'.py'
607 608 else:
608 609 raise ValueError('Invalid notebook format: %s' % new_format)
609 610 with io.open(old_fname, 'r', encoding='utf-8') as f:
610 611 nb = current.read(f, old_format)
611 612 with io.open(new_fname, 'w', encoding='utf-8') as f:
612 613 current.write(nb, f, new_format)
@@ -1,1030 +1,1035 b''
1 1 """Implementation of execution-related magic functions.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2012 The IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 # Stdlib
16 16 import __builtin__ as builtin_mod
17 17 import bdb
18 18 import os
19 19 import sys
20 20 import time
21 21 from StringIO import StringIO
22 22
23 23 # cProfile was added in Python2.5
24 24 try:
25 25 import cProfile as profile
26 26 import pstats
27 27 except ImportError:
28 28 # profile isn't bundled by default in Debian for license reasons
29 29 try:
30 30 import profile, pstats
31 31 except ImportError:
32 32 profile = pstats = None
33 33
34 34 # Our own packages
35 35 from IPython.core import debugger, oinspect
36 36 from IPython.core import magic_arguments
37 37 from IPython.core import page
38 38 from IPython.core.error import UsageError
39 39 from IPython.core.macro import Macro
40 40 from IPython.core.magic import (Magics, magics_class, line_magic, cell_magic,
41 41 line_cell_magic, on_off, needs_local_scope)
42 42 from IPython.testing.skipdoctest import skip_doctest
43 43 from IPython.utils import py3compat
44 44 from IPython.utils.io import capture_output
45 45 from IPython.utils.ipstruct import Struct
46 46 from IPython.utils.module_paths import find_mod
47 47 from IPython.utils.path import get_py_filename, unquote_filename, shellglob
48 48 from IPython.utils.timing import clock, clock2
49 49 from IPython.utils.warn import warn, error
50 50
51 51
52 52 #-----------------------------------------------------------------------------
53 53 # Magic implementation classes
54 54 #-----------------------------------------------------------------------------
55 55
56 56 @magics_class
57 57 class ExecutionMagics(Magics):
58 58 """Magics related to code execution, debugging, profiling, etc.
59 59
60 60 """
61 61
62 62 def __init__(self, shell):
63 63 super(ExecutionMagics, self).__init__(shell)
64 64 if profile is None:
65 65 self.prun = self.profile_missing_notice
66 66 # Default execution function used to actually run user code.
67 67 self.default_runner = None
68 68
69 69 def profile_missing_notice(self, *args, **kwargs):
70 70 error("""\
71 71 The profile module could not be found. It has been removed from the standard
72 72 python packages because of its non-free license. To use profiling, install the
73 73 python-profiler package from non-free.""")
74 74
75 75 @skip_doctest
76 76 @line_cell_magic
77 77 def prun(self, parameter_s='', cell=None, user_mode=True,
78 78 opts=None,arg_lst=None,prog_ns=None):
79 79
80 80 """Run a statement through the python code profiler.
81 81
82 82 Usage, in line mode:
83 83 %prun [options] statement
84 84
85 85 Usage, in cell mode:
86 86 %%prun [options] [statement]
87 87 code...
88 88 code...
89 89
90 90 In cell mode, the additional code lines are appended to the (possibly
91 91 empty) statement in the first line. Cell mode allows you to easily
92 92 profile multiline blocks without having to put them in a separate
93 93 function.
94 94
95 95 The given statement (which doesn't require quote marks) is run via the
96 96 python profiler in a manner similar to the profile.run() function.
97 97 Namespaces are internally managed to work correctly; profile.run
98 98 cannot be used in IPython because it makes certain assumptions about
99 99 namespaces which do not hold under IPython.
100 100
101 101 Options:
102 102
103 103 -l <limit>: you can place restrictions on what or how much of the
104 104 profile gets printed. The limit value can be:
105 105
106 106 * A string: only information for function names containing this string
107 107 is printed.
108 108
109 109 * An integer: only these many lines are printed.
110 110
111 111 * A float (between 0 and 1): this fraction of the report is printed
112 112 (for example, use a limit of 0.4 to see the topmost 40% only).
113 113
114 114 You can combine several limits with repeated use of the option. For
115 115 example, '-l __init__ -l 5' will print only the topmost 5 lines of
116 116 information about class constructors.
117 117
118 118 -r: return the pstats.Stats object generated by the profiling. This
119 119 object has all the information about the profile in it, and you can
120 120 later use it for further analysis or in other functions.
121 121
122 122 -s <key>: sort profile by given key. You can provide more than one key
123 123 by using the option several times: '-s key1 -s key2 -s key3...'. The
124 124 default sorting key is 'time'.
125 125
126 126 The following is copied verbatim from the profile documentation
127 127 referenced below:
128 128
129 129 When more than one key is provided, additional keys are used as
130 130 secondary criteria when the there is equality in all keys selected
131 131 before them.
132 132
133 133 Abbreviations can be used for any key names, as long as the
134 134 abbreviation is unambiguous. The following are the keys currently
135 135 defined:
136 136
137 137 Valid Arg Meaning
138 138 "calls" call count
139 139 "cumulative" cumulative time
140 140 "file" file name
141 141 "module" file name
142 142 "pcalls" primitive call count
143 143 "line" line number
144 144 "name" function name
145 145 "nfl" name/file/line
146 146 "stdname" standard name
147 147 "time" internal time
148 148
149 149 Note that all sorts on statistics are in descending order (placing
150 150 most time consuming items first), where as name, file, and line number
151 151 searches are in ascending order (i.e., alphabetical). The subtle
152 152 distinction between "nfl" and "stdname" is that the standard name is a
153 153 sort of the name as printed, which means that the embedded line
154 154 numbers get compared in an odd way. For example, lines 3, 20, and 40
155 155 would (if the file names were the same) appear in the string order
156 156 "20" "3" and "40". In contrast, "nfl" does a numeric compare of the
157 157 line numbers. In fact, sort_stats("nfl") is the same as
158 158 sort_stats("name", "file", "line").
159 159
160 160 -T <filename>: save profile results as shown on screen to a text
161 161 file. The profile is still shown on screen.
162 162
163 163 -D <filename>: save (via dump_stats) profile statistics to given
164 164 filename. This data is in a format understood by the pstats module, and
165 165 is generated by a call to the dump_stats() method of profile
166 166 objects. The profile is still shown on screen.
167 167
168 168 -q: suppress output to the pager. Best used with -T and/or -D above.
169 169
170 170 If you want to run complete programs under the profiler's control, use
171 171 '%run -p [prof_opts] filename.py [args to program]' where prof_opts
172 172 contains profiler specific options as described here.
173 173
174 174 You can read the complete documentation for the profile module with::
175 175
176 176 In [1]: import profile; profile.help()
177 177 """
178 178
179 179 opts_def = Struct(D=[''],l=[],s=['time'],T=[''])
180 180
181 181 if user_mode: # regular user call
182 182 opts,arg_str = self.parse_options(parameter_s,'D:l:rs:T:q',
183 183 list_all=True, posix=False)
184 184 namespace = self.shell.user_ns
185 185 if cell is not None:
186 186 arg_str += '\n' + cell
187 187 else: # called to run a program by %run -p
188 188 try:
189 189 filename = get_py_filename(arg_lst[0])
190 190 except IOError as e:
191 191 try:
192 192 msg = str(e)
193 193 except UnicodeError:
194 194 msg = e.message
195 195 error(msg)
196 196 return
197 197
198 198 arg_str = 'execfile(filename,prog_ns)'
199 199 namespace = {
200 200 'execfile': self.shell.safe_execfile,
201 201 'prog_ns': prog_ns,
202 202 'filename': filename
203 203 }
204 204
205 205 opts.merge(opts_def)
206 206
207 207 prof = profile.Profile()
208 208 try:
209 209 prof = prof.runctx(arg_str,namespace,namespace)
210 210 sys_exit = ''
211 211 except SystemExit:
212 212 sys_exit = """*** SystemExit exception caught in code being profiled."""
213 213
214 214 stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s)
215 215
216 216 lims = opts.l
217 217 if lims:
218 218 lims = [] # rebuild lims with ints/floats/strings
219 219 for lim in opts.l:
220 220 try:
221 221 lims.append(int(lim))
222 222 except ValueError:
223 223 try:
224 224 lims.append(float(lim))
225 225 except ValueError:
226 226 lims.append(lim)
227 227
228 228 # Trap output.
229 229 stdout_trap = StringIO()
230 230 stats_stream = stats.stream
231 231 try:
232 232 stats.stream = stdout_trap
233 233 stats.print_stats(*lims)
234 234 finally:
235 235 stats.stream = stats_stream
236 236
237 237 output = stdout_trap.getvalue()
238 238 output = output.rstrip()
239 239
240 240 if 'q' not in opts:
241 241 page.page(output)
242 242 print sys_exit,
243 243
244 244 dump_file = opts.D[0]
245 245 text_file = opts.T[0]
246 246 if dump_file:
247 247 dump_file = unquote_filename(dump_file)
248 248 prof.dump_stats(dump_file)
249 249 print '\n*** Profile stats marshalled to file',\
250 250 repr(dump_file)+'.',sys_exit
251 251 if text_file:
252 252 text_file = unquote_filename(text_file)
253 253 pfile = open(text_file,'w')
254 254 pfile.write(output)
255 255 pfile.close()
256 256 print '\n*** Profile printout saved to text file',\
257 257 repr(text_file)+'.',sys_exit
258 258
259 259 if 'r' in opts:
260 260 return stats
261 261 else:
262 262 return None
263 263
264 264 @line_magic
265 265 def pdb(self, parameter_s=''):
266 266 """Control the automatic calling of the pdb interactive debugger.
267 267
268 268 Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without
269 269 argument it works as a toggle.
270 270
271 271 When an exception is triggered, IPython can optionally call the
272 272 interactive pdb debugger after the traceback printout. %pdb toggles
273 273 this feature on and off.
274 274
275 275 The initial state of this feature is set in your configuration
276 276 file (the option is ``InteractiveShell.pdb``).
277 277
278 278 If you want to just activate the debugger AFTER an exception has fired,
279 279 without having to type '%pdb on' and rerunning your code, you can use
280 280 the %debug magic."""
281 281
282 282 par = parameter_s.strip().lower()
283 283
284 284 if par:
285 285 try:
286 286 new_pdb = {'off':0,'0':0,'on':1,'1':1}[par]
287 287 except KeyError:
288 288 print ('Incorrect argument. Use on/1, off/0, '
289 289 'or nothing for a toggle.')
290 290 return
291 291 else:
292 292 # toggle
293 293 new_pdb = not self.shell.call_pdb
294 294
295 295 # set on the shell
296 296 self.shell.call_pdb = new_pdb
297 297 print 'Automatic pdb calling has been turned',on_off(new_pdb)
298 298
299 299 @line_magic
300 300 def debug(self, parameter_s=''):
301 301 """Activate the interactive debugger in post-mortem mode.
302 302
303 303 If an exception has just occurred, this lets you inspect its stack
304 304 frames interactively. Note that this will always work only on the last
305 305 traceback that occurred, so you must call this quickly after an
306 306 exception that you wish to inspect has fired, because if another one
307 307 occurs, it clobbers the previous one.
308 308
309 309 If you want IPython to automatically do this on every exception, see
310 310 the %pdb magic for more details.
311 311 """
312 312 self.shell.debugger(force=True)
313 313
314 314 @line_magic
315 315 def tb(self, s):
316 316 """Print the last traceback with the currently active exception mode.
317 317
318 318 See %xmode for changing exception reporting modes."""
319 319 self.shell.showtraceback()
320 320
321 321 @skip_doctest
322 322 @line_magic
323 323 def run(self, parameter_s='', runner=None,
324 324 file_finder=get_py_filename):
325 325 """Run the named file inside IPython as a program.
326 326
327 327 Usage:\\
328 328 %run [-n -i -t [-N<N>] -d [-b<N>] -p [profile options] -G] file [args]
329 329
330 330 Parameters after the filename are passed as command-line arguments to
331 331 the program (put in sys.argv). Then, control returns to IPython's
332 332 prompt.
333 333
334 334 This is similar to running at a system prompt:\\
335 335 $ python file args\\
336 336 but with the advantage of giving you IPython's tracebacks, and of
337 337 loading all variables into your interactive namespace for further use
338 338 (unless -p is used, see below).
339 339
340 340 The file is executed in a namespace initially consisting only of
341 341 __name__=='__main__' and sys.argv constructed as indicated. It thus
342 342 sees its environment as if it were being run as a stand-alone program
343 343 (except for sharing global objects such as previously imported
344 344 modules). But after execution, the IPython interactive namespace gets
345 345 updated with all variables defined in the program (except for __name__
346 346 and sys.argv). This allows for very convenient loading of code for
347 347 interactive work, while giving each program a 'clean sheet' to run in.
348 348
349 349 Arguments are expanded using shell-like glob match. Patterns
350 350 '*', '?', '[seq]' and '[!seq]' can be used. Additionally,
351 351 tilde '~' will be expanded into user's home directory. Unlike
352 352 real shells, quotation does not suppress expansions. Use
353 353 *two* back slashes (e.g., '\\\\*') to suppress expansions.
354 354 To completely disable these expansions, you can use -G flag.
355 355
356 356 Options:
357 357
358 358 -n: __name__ is NOT set to '__main__', but to the running file's name
359 359 without extension (as python does under import). This allows running
360 360 scripts and reloading the definitions in them without calling code
361 361 protected by an ' if __name__ == "__main__" ' clause.
362 362
363 363 -i: run the file in IPython's namespace instead of an empty one. This
364 364 is useful if you are experimenting with code written in a text editor
365 365 which depends on variables defined interactively.
366 366
367 367 -e: ignore sys.exit() calls or SystemExit exceptions in the script
368 368 being run. This is particularly useful if IPython is being used to
369 369 run unittests, which always exit with a sys.exit() call. In such
370 370 cases you are interested in the output of the test results, not in
371 371 seeing a traceback of the unittest module.
372 372
373 373 -t: print timing information at the end of the run. IPython will give
374 374 you an estimated CPU time consumption for your script, which under
375 375 Unix uses the resource module to avoid the wraparound problems of
376 376 time.clock(). Under Unix, an estimate of time spent on system tasks
377 377 is also given (for Windows platforms this is reported as 0.0).
378 378
379 379 If -t is given, an additional -N<N> option can be given, where <N>
380 380 must be an integer indicating how many times you want the script to
381 381 run. The final timing report will include total and per run results.
382 382
383 383 For example (testing the script uniq_stable.py)::
384 384
385 385 In [1]: run -t uniq_stable
386 386
387 387 IPython CPU timings (estimated):\\
388 388 User : 0.19597 s.\\
389 389 System: 0.0 s.\\
390 390
391 391 In [2]: run -t -N5 uniq_stable
392 392
393 393 IPython CPU timings (estimated):\\
394 394 Total runs performed: 5\\
395 395 Times : Total Per run\\
396 396 User : 0.910862 s, 0.1821724 s.\\
397 397 System: 0.0 s, 0.0 s.
398 398
399 399 -d: run your program under the control of pdb, the Python debugger.
400 400 This allows you to execute your program step by step, watch variables,
401 401 etc. Internally, what IPython does is similar to calling:
402 402
403 403 pdb.run('execfile("YOURFILENAME")')
404 404
405 405 with a breakpoint set on line 1 of your file. You can change the line
406 406 number for this automatic breakpoint to be <N> by using the -bN option
407 407 (where N must be an integer). For example::
408 408
409 409 %run -d -b40 myscript
410 410
411 411 will set the first breakpoint at line 40 in myscript.py. Note that
412 412 the first breakpoint must be set on a line which actually does
413 413 something (not a comment or docstring) for it to stop execution.
414 414
415 415 When the pdb debugger starts, you will see a (Pdb) prompt. You must
416 416 first enter 'c' (without quotes) to start execution up to the first
417 417 breakpoint.
418 418
419 419 Entering 'help' gives information about the use of the debugger. You
420 420 can easily see pdb's full documentation with "import pdb;pdb.help()"
421 421 at a prompt.
422 422
423 423 -p: run program under the control of the Python profiler module (which
424 424 prints a detailed report of execution times, function calls, etc).
425 425
426 426 You can pass other options after -p which affect the behavior of the
427 427 profiler itself. See the docs for %prun for details.
428 428
429 429 In this mode, the program's variables do NOT propagate back to the
430 430 IPython interactive namespace (because they remain in the namespace
431 431 where the profiler executes them).
432 432
433 433 Internally this triggers a call to %prun, see its documentation for
434 434 details on the options available specifically for profiling.
435 435
436 436 There is one special usage for which the text above doesn't apply:
437 437 if the filename ends with .ipy, the file is run as ipython script,
438 438 just as if the commands were written on IPython prompt.
439 439
440 440 -m: specify module name to load instead of script path. Similar to
441 441 the -m option for the python interpreter. Use this option last if you
442 442 want to combine with other %run options. Unlike the python interpreter
443 443 only source modules are allowed no .pyc or .pyo files.
444 444 For example::
445 445
446 446 %run -m example
447 447
448 448 will run the example module.
449 449
450 450 -G: disable shell-like glob expansion of arguments.
451 451
452 452 """
453 453
454 454 # get arguments and set sys.argv for program to be run.
455 455 opts, arg_lst = self.parse_options(parameter_s,
456 456 'nidtN:b:pD:l:rs:T:em:G',
457 457 mode='list', list_all=1)
458 458 if "m" in opts:
459 459 modulename = opts["m"][0]
460 460 modpath = find_mod(modulename)
461 461 if modpath is None:
462 462 warn('%r is not a valid modulename on sys.path'%modulename)
463 463 return
464 464 arg_lst = [modpath] + arg_lst
465 465 try:
466 466 filename = file_finder(arg_lst[0])
467 467 except IndexError:
468 468 warn('you must provide at least a filename.')
469 469 print '\n%run:\n', oinspect.getdoc(self.run)
470 470 return
471 471 except IOError as e:
472 472 try:
473 473 msg = str(e)
474 474 except UnicodeError:
475 475 msg = e.message
476 476 error(msg)
477 477 return
478 478
479 479 if filename.lower().endswith('.ipy'):
480 480 self.shell.safe_execfile_ipy(filename)
481 481 return
482 482
483 483 # Control the response to exit() calls made by the script being run
484 484 exit_ignore = 'e' in opts
485 485
486 486 # Make sure that the running script gets a proper sys.argv as if it
487 487 # were run from a system shell.
488 488 save_argv = sys.argv # save it for later restoring
489 489
490 490 if 'G' in opts:
491 491 args = arg_lst[1:]
492 492 else:
493 493 # tilde and glob expansion
494 494 args = shellglob(map(os.path.expanduser, arg_lst[1:]))
495 495
496 496 sys.argv = [filename] + args # put in the proper filename
497 497 # protect sys.argv from potential unicode strings on Python 2:
498 498 if not py3compat.PY3:
499 499 sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ]
500 500
501 501 if 'i' in opts:
502 502 # Run in user's interactive namespace
503 503 prog_ns = self.shell.user_ns
504 504 __name__save = self.shell.user_ns['__name__']
505 505 prog_ns['__name__'] = '__main__'
506 506 main_mod = self.shell.new_main_mod(prog_ns)
507 507 else:
508 508 # Run in a fresh, empty namespace
509 509 if 'n' in opts:
510 510 name = os.path.splitext(os.path.basename(filename))[0]
511 511 else:
512 512 name = '__main__'
513 513
514 514 main_mod = self.shell.new_main_mod()
515 515 prog_ns = main_mod.__dict__
516 516 prog_ns['__name__'] = name
517 517
518 518 # Since '%run foo' emulates 'python foo.py' at the cmd line, we must
519 519 # set the __file__ global in the script's namespace
520 520 prog_ns['__file__'] = filename
521 521
522 522 # pickle fix. See interactiveshell for an explanation. But we need to
523 523 # make sure that, if we overwrite __main__, we replace it at the end
524 524 main_mod_name = prog_ns['__name__']
525 525
526 526 if main_mod_name == '__main__':
527 527 restore_main = sys.modules['__main__']
528 528 else:
529 529 restore_main = False
530 530
531 531 # This needs to be undone at the end to prevent holding references to
532 532 # every single object ever created.
533 533 sys.modules[main_mod_name] = main_mod
534 534
535 535 try:
536 536 stats = None
537 537 with self.shell.readline_no_record:
538 538 if 'p' in opts:
539 539 stats = self.prun('', None, False, opts, arg_lst, prog_ns)
540 540 else:
541 541 if 'd' in opts:
542 542 deb = debugger.Pdb(self.shell.colors)
543 543 # reset Breakpoint state, which is moronically kept
544 544 # in a class
545 545 bdb.Breakpoint.next = 1
546 546 bdb.Breakpoint.bplist = {}
547 547 bdb.Breakpoint.bpbynumber = [None]
548 548 # Set an initial breakpoint to stop execution
549 549 maxtries = 10
550 550 bp = int(opts.get('b', [1])[0])
551 551 checkline = deb.checkline(filename, bp)
552 552 if not checkline:
553 553 for bp in range(bp + 1, bp + maxtries + 1):
554 554 if deb.checkline(filename, bp):
555 555 break
556 556 else:
557 557 msg = ("\nI failed to find a valid line to set "
558 558 "a breakpoint\n"
559 559 "after trying up to line: %s.\n"
560 560 "Please set a valid breakpoint manually "
561 561 "with the -b option." % bp)
562 562 error(msg)
563 563 return
564 564 # if we find a good linenumber, set the breakpoint
565 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 571 # Start file run
567 572 print "NOTE: Enter 'c' at the",
568 573 print "%s prompt to start your script." % deb.prompt
569 574 ns = {'execfile': py3compat.execfile, 'prog_ns': prog_ns}
570 575 try:
571 576 #save filename so it can be used by methods on the deb object
572 577 deb._exec_filename = filename
573 578 deb.run('execfile("%s", prog_ns)' % filename, ns)
574 579
575 580 except:
576 581 etype, value, tb = sys.exc_info()
577 582 # Skip three frames in the traceback: the %run one,
578 583 # one inside bdb.py, and the command-line typed by the
579 584 # user (run by exec in pdb itself).
580 585 self.shell.InteractiveTB(etype, value, tb, tb_offset=3)
581 586 else:
582 587 if runner is None:
583 588 runner = self.default_runner
584 589 if runner is None:
585 590 runner = self.shell.safe_execfile
586 591 if 't' in opts:
587 592 # timed execution
588 593 try:
589 594 nruns = int(opts['N'][0])
590 595 if nruns < 1:
591 596 error('Number of runs must be >=1')
592 597 return
593 598 except (KeyError):
594 599 nruns = 1
595 600 twall0 = time.time()
596 601 if nruns == 1:
597 602 t0 = clock2()
598 603 runner(filename, prog_ns, prog_ns,
599 604 exit_ignore=exit_ignore)
600 605 t1 = clock2()
601 606 t_usr = t1[0] - t0[0]
602 607 t_sys = t1[1] - t0[1]
603 608 print "\nIPython CPU timings (estimated):"
604 609 print " User : %10.2f s." % t_usr
605 610 print " System : %10.2f s." % t_sys
606 611 else:
607 612 runs = range(nruns)
608 613 t0 = clock2()
609 614 for nr in runs:
610 615 runner(filename, prog_ns, prog_ns,
611 616 exit_ignore=exit_ignore)
612 617 t1 = clock2()
613 618 t_usr = t1[0] - t0[0]
614 619 t_sys = t1[1] - t0[1]
615 620 print "\nIPython CPU timings (estimated):"
616 621 print "Total runs performed:", nruns
617 622 print " Times : %10.2f %10.2f" % ('Total', 'Per run')
618 623 print " User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns)
619 624 print " System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns)
620 625 twall1 = time.time()
621 626 print "Wall time: %10.2f s." % (twall1 - twall0)
622 627
623 628 else:
624 629 # regular execution
625 630 runner(filename, prog_ns, prog_ns, exit_ignore=exit_ignore)
626 631
627 632 if 'i' in opts:
628 633 self.shell.user_ns['__name__'] = __name__save
629 634 else:
630 635 # The shell MUST hold a reference to prog_ns so after %run
631 636 # exits, the python deletion mechanism doesn't zero it out
632 637 # (leaving dangling references).
633 638 self.shell.cache_main_mod(prog_ns, filename)
634 639 # update IPython interactive namespace
635 640
636 641 # Some forms of read errors on the file may mean the
637 642 # __name__ key was never set; using pop we don't have to
638 643 # worry about a possible KeyError.
639 644 prog_ns.pop('__name__', None)
640 645
641 646 self.shell.user_ns.update(prog_ns)
642 647 finally:
643 648 # It's a bit of a mystery why, but __builtins__ can change from
644 649 # being a module to becoming a dict missing some key data after
645 650 # %run. As best I can see, this is NOT something IPython is doing
646 651 # at all, and similar problems have been reported before:
647 652 # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html
648 653 # Since this seems to be done by the interpreter itself, the best
649 654 # we can do is to at least restore __builtins__ for the user on
650 655 # exit.
651 656 self.shell.user_ns['__builtins__'] = builtin_mod
652 657
653 658 # Ensure key global structures are restored
654 659 sys.argv = save_argv
655 660 if restore_main:
656 661 sys.modules['__main__'] = restore_main
657 662 else:
658 663 # Remove from sys.modules the reference to main_mod we'd
659 664 # added. Otherwise it will trap references to objects
660 665 # contained therein.
661 666 del sys.modules[main_mod_name]
662 667
663 668 return stats
664 669
665 670 @skip_doctest
666 671 @line_cell_magic
667 672 def timeit(self, line='', cell=None):
668 673 """Time execution of a Python statement or expression
669 674
670 675 Usage, in line mode:
671 676 %timeit [-n<N> -r<R> [-t|-c]] statement
672 677 or in cell mode:
673 678 %%timeit [-n<N> -r<R> [-t|-c]] setup_code
674 679 code
675 680 code...
676 681
677 682 Time execution of a Python statement or expression using the timeit
678 683 module. This function can be used both as a line and cell magic:
679 684
680 685 - In line mode you can time a single-line statement (though multiple
681 686 ones can be chained with using semicolons).
682 687
683 688 - In cell mode, the statement in the first line is used as setup code
684 689 (executed but not timed) and the body of the cell is timed. The cell
685 690 body has access to any variables created in the setup code.
686 691
687 692 Options:
688 693 -n<N>: execute the given statement <N> times in a loop. If this value
689 694 is not given, a fitting value is chosen.
690 695
691 696 -r<R>: repeat the loop iteration <R> times and take the best result.
692 697 Default: 3
693 698
694 699 -t: use time.time to measure the time, which is the default on Unix.
695 700 This function measures wall time.
696 701
697 702 -c: use time.clock to measure the time, which is the default on
698 703 Windows and measures wall time. On Unix, resource.getrusage is used
699 704 instead and returns the CPU user time.
700 705
701 706 -p<P>: use a precision of <P> digits to display the timing result.
702 707 Default: 3
703 708
704 709
705 710 Examples
706 711 --------
707 712 ::
708 713
709 714 In [1]: %timeit pass
710 715 10000000 loops, best of 3: 53.3 ns per loop
711 716
712 717 In [2]: u = None
713 718
714 719 In [3]: %timeit u is None
715 720 10000000 loops, best of 3: 184 ns per loop
716 721
717 722 In [4]: %timeit -r 4 u == None
718 723 1000000 loops, best of 4: 242 ns per loop
719 724
720 725 In [5]: import time
721 726
722 727 In [6]: %timeit -n1 time.sleep(2)
723 728 1 loops, best of 3: 2 s per loop
724 729
725 730
726 731 The times reported by %timeit will be slightly higher than those
727 732 reported by the timeit.py script when variables are accessed. This is
728 733 due to the fact that %timeit executes the statement in the namespace
729 734 of the shell, compared with timeit.py, which uses a single setup
730 735 statement to import function or create variables. Generally, the bias
731 736 does not matter as long as results from timeit.py are not mixed with
732 737 those from %timeit."""
733 738
734 739 import timeit
735 740 import math
736 741
737 742 # XXX: Unfortunately the unicode 'micro' symbol can cause problems in
738 743 # certain terminals. Until we figure out a robust way of
739 744 # auto-detecting if the terminal can deal with it, use plain 'us' for
740 745 # microseconds. I am really NOT happy about disabling the proper
741 746 # 'micro' prefix, but crashing is worse... If anyone knows what the
742 747 # right solution for this is, I'm all ears...
743 748 #
744 749 # Note: using
745 750 #
746 751 # s = u'\xb5'
747 752 # s.encode(sys.getdefaultencoding())
748 753 #
749 754 # is not sufficient, as I've seen terminals where that fails but
750 755 # print s
751 756 #
752 757 # succeeds
753 758 #
754 759 # See bug: https://bugs.launchpad.net/ipython/+bug/348466
755 760
756 761 #units = [u"s", u"ms",u'\xb5',"ns"]
757 762 units = [u"s", u"ms",u'us',"ns"]
758 763
759 764 scaling = [1, 1e3, 1e6, 1e9]
760 765
761 766 opts, stmt = self.parse_options(line,'n:r:tcp:',
762 767 posix=False, strict=False)
763 768 if stmt == "" and cell is None:
764 769 return
765 770 timefunc = timeit.default_timer
766 771 number = int(getattr(opts, "n", 0))
767 772 repeat = int(getattr(opts, "r", timeit.default_repeat))
768 773 precision = int(getattr(opts, "p", 3))
769 774 if hasattr(opts, "t"):
770 775 timefunc = time.time
771 776 if hasattr(opts, "c"):
772 777 timefunc = clock
773 778
774 779 timer = timeit.Timer(timer=timefunc)
775 780 # this code has tight coupling to the inner workings of timeit.Timer,
776 781 # but is there a better way to achieve that the code stmt has access
777 782 # to the shell namespace?
778 783 transform = self.shell.input_splitter.transform_cell
779 784 if cell is None:
780 785 # called as line magic
781 786 setup = 'pass'
782 787 stmt = timeit.reindent(transform(stmt), 8)
783 788 else:
784 789 setup = timeit.reindent(transform(stmt), 4)
785 790 stmt = timeit.reindent(transform(cell), 8)
786 791
787 792 # From Python 3.3, this template uses new-style string formatting.
788 793 if sys.version_info >= (3, 3):
789 794 src = timeit.template.format(stmt=stmt, setup=setup)
790 795 else:
791 796 src = timeit.template % dict(stmt=stmt, setup=setup)
792 797
793 798 # Track compilation time so it can be reported if too long
794 799 # Minimum time above which compilation time will be reported
795 800 tc_min = 0.1
796 801
797 802 t0 = clock()
798 803 code = compile(src, "<magic-timeit>", "exec")
799 804 tc = clock()-t0
800 805
801 806 ns = {}
802 807 exec code in self.shell.user_ns, ns
803 808 timer.inner = ns["inner"]
804 809
805 810 if number == 0:
806 811 # determine number so that 0.2 <= total time < 2.0
807 812 number = 1
808 813 for i in range(1, 10):
809 814 if timer.timeit(number) >= 0.2:
810 815 break
811 816 number *= 10
812 817
813 818 best = min(timer.repeat(repeat, number)) / number
814 819
815 820 if best > 0.0 and best < 1000.0:
816 821 order = min(-int(math.floor(math.log10(best)) // 3), 3)
817 822 elif best >= 1000.0:
818 823 order = 0
819 824 else:
820 825 order = 3
821 826 print u"%d loops, best of %d: %.*g %s per loop" % (number, repeat,
822 827 precision,
823 828 best * scaling[order],
824 829 units[order])
825 830 if tc > tc_min:
826 831 print "Compiler time: %.2f s" % tc
827 832
828 833 @skip_doctest
829 834 @needs_local_scope
830 835 @line_magic
831 836 def time(self,parameter_s, local_ns=None):
832 837 """Time execution of a Python statement or expression.
833 838
834 839 The CPU and wall clock times are printed, and the value of the
835 840 expression (if any) is returned. Note that under Win32, system time
836 841 is always reported as 0, since it can not be measured.
837 842
838 843 This function provides very basic timing functionality. In Python
839 844 2.3, the timeit module offers more control and sophistication, so this
840 845 could be rewritten to use it (patches welcome).
841 846
842 847 Examples
843 848 --------
844 849 ::
845 850
846 851 In [1]: time 2**128
847 852 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
848 853 Wall time: 0.00
849 854 Out[1]: 340282366920938463463374607431768211456L
850 855
851 856 In [2]: n = 1000000
852 857
853 858 In [3]: time sum(range(n))
854 859 CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s
855 860 Wall time: 1.37
856 861 Out[3]: 499999500000L
857 862
858 863 In [4]: time print 'hello world'
859 864 hello world
860 865 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
861 866 Wall time: 0.00
862 867
863 868 Note that the time needed by Python to compile the given expression
864 869 will be reported if it is more than 0.1s. In this example, the
865 870 actual exponentiation is done by Python at compilation time, so while
866 871 the expression can take a noticeable amount of time to compute, that
867 872 time is purely due to the compilation:
868 873
869 874 In [5]: time 3**9999;
870 875 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
871 876 Wall time: 0.00 s
872 877
873 878 In [6]: time 3**999999;
874 879 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
875 880 Wall time: 0.00 s
876 881 Compiler : 0.78 s
877 882 """
878 883
879 884 # fail immediately if the given expression can't be compiled
880 885
881 886 expr = self.shell.prefilter(parameter_s,False)
882 887
883 888 # Minimum time above which compilation time will be reported
884 889 tc_min = 0.1
885 890
886 891 try:
887 892 mode = 'eval'
888 893 t0 = clock()
889 894 code = compile(expr,'<timed eval>',mode)
890 895 tc = clock()-t0
891 896 except SyntaxError:
892 897 mode = 'exec'
893 898 t0 = clock()
894 899 code = compile(expr,'<timed exec>',mode)
895 900 tc = clock()-t0
896 901 # skew measurement as little as possible
897 902 glob = self.shell.user_ns
898 903 wtime = time.time
899 904 # time execution
900 905 wall_st = wtime()
901 906 if mode=='eval':
902 907 st = clock2()
903 908 out = eval(code, glob, local_ns)
904 909 end = clock2()
905 910 else:
906 911 st = clock2()
907 912 exec code in glob, local_ns
908 913 end = clock2()
909 914 out = None
910 915 wall_end = wtime()
911 916 # Compute actual times and report
912 917 wall_time = wall_end-wall_st
913 918 cpu_user = end[0]-st[0]
914 919 cpu_sys = end[1]-st[1]
915 920 cpu_tot = cpu_user+cpu_sys
916 921 print "CPU times: user %.2f s, sys: %.2f s, total: %.2f s" % \
917 922 (cpu_user,cpu_sys,cpu_tot)
918 923 print "Wall time: %.2f s" % wall_time
919 924 if tc > tc_min:
920 925 print "Compiler : %.2f s" % tc
921 926 return out
922 927
923 928 @skip_doctest
924 929 @line_magic
925 930 def macro(self, parameter_s=''):
926 931 """Define a macro for future re-execution. It accepts ranges of history,
927 932 filenames or string objects.
928 933
929 934 Usage:\\
930 935 %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ...
931 936
932 937 Options:
933 938
934 939 -r: use 'raw' input. By default, the 'processed' history is used,
935 940 so that magics are loaded in their transformed version to valid
936 941 Python. If this option is given, the raw input as typed as the
937 942 command line is used instead.
938 943
939 944 This will define a global variable called `name` which is a string
940 945 made of joining the slices and lines you specify (n1,n2,... numbers
941 946 above) from your input history into a single string. This variable
942 947 acts like an automatic function which re-executes those lines as if
943 948 you had typed them. You just type 'name' at the prompt and the code
944 949 executes.
945 950
946 951 The syntax for indicating input ranges is described in %history.
947 952
948 953 Note: as a 'hidden' feature, you can also use traditional python slice
949 954 notation, where N:M means numbers N through M-1.
950 955
951 956 For example, if your history contains (%hist prints it)::
952 957
953 958 44: x=1
954 959 45: y=3
955 960 46: z=x+y
956 961 47: print x
957 962 48: a=5
958 963 49: print 'x',x,'y',y
959 964
960 965 you can create a macro with lines 44 through 47 (included) and line 49
961 966 called my_macro with::
962 967
963 968 In [55]: %macro my_macro 44-47 49
964 969
965 970 Now, typing `my_macro` (without quotes) will re-execute all this code
966 971 in one pass.
967 972
968 973 You don't need to give the line-numbers in order, and any given line
969 974 number can appear multiple times. You can assemble macros with any
970 975 lines from your input history in any order.
971 976
972 977 The macro is a simple object which holds its value in an attribute,
973 978 but IPython's display system checks for macros and executes them as
974 979 code instead of printing them when you type their name.
975 980
976 981 You can view a macro's contents by explicitly printing it with::
977 982
978 983 print macro_name
979 984
980 985 """
981 986 opts,args = self.parse_options(parameter_s,'r',mode='list')
982 987 if not args: # List existing macros
983 988 return sorted(k for k,v in self.shell.user_ns.iteritems() if\
984 989 isinstance(v, Macro))
985 990 if len(args) == 1:
986 991 raise UsageError(
987 992 "%macro insufficient args; usage '%macro name n1-n2 n3-4...")
988 993 name, codefrom = args[0], " ".join(args[1:])
989 994
990 995 #print 'rng',ranges # dbg
991 996 try:
992 997 lines = self.shell.find_user_code(codefrom, 'r' in opts)
993 998 except (ValueError, TypeError) as e:
994 999 print e.args[0]
995 1000 return
996 1001 macro = Macro(lines)
997 1002 self.shell.define_macro(name, macro)
998 1003 print 'Macro `%s` created. To execute, type its name (without quotes).' % name
999 1004 print '=== Macro contents: ==='
1000 1005 print macro,
1001 1006
1002 1007 @magic_arguments.magic_arguments()
1003 1008 @magic_arguments.argument('output', type=str, default='', nargs='?',
1004 1009 help="""The name of the variable in which to store output.
1005 1010 This is a utils.io.CapturedIO object with stdout/err attributes
1006 1011 for the text of the captured output.
1007 1012
1008 1013 CapturedOutput also has a show() method for displaying the output,
1009 1014 and __call__ as well, so you can use that to quickly display the
1010 1015 output.
1011 1016
1012 1017 If unspecified, captured output is discarded.
1013 1018 """
1014 1019 )
1015 1020 @magic_arguments.argument('--no-stderr', action="store_true",
1016 1021 help="""Don't capture stderr."""
1017 1022 )
1018 1023 @magic_arguments.argument('--no-stdout', action="store_true",
1019 1024 help="""Don't capture stdout."""
1020 1025 )
1021 1026 @cell_magic
1022 1027 def capture(self, line, cell):
1023 1028 """run the cell, capturing stdout/err"""
1024 1029 args = magic_arguments.parse_argstring(self.capture, line)
1025 1030 out = not args.no_stdout
1026 1031 err = not args.no_stderr
1027 1032 with capture_output(out, err) as io:
1028 1033 self.shell.run_cell(cell)
1029 1034 if args.output:
1030 1035 self.shell.user_ns[args.output] = io
@@ -1,76 +1,92 b''
1 1 """Implementation of magic functions for the extension machinery.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2012 The IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 # Stdlib
16 16 import os
17 17
18 18 # Our own packages
19 19 from IPython.core.error import UsageError
20 20 from IPython.core.magic import Magics, magics_class, line_magic
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Magic implementation classes
24 24 #-----------------------------------------------------------------------------
25 25
26 26 @magics_class
27 27 class ExtensionMagics(Magics):
28 28 """Magics to manage the IPython extensions system."""
29 29
30 30 @line_magic
31 31 def install_ext(self, parameter_s=''):
32 32 """Download and install an extension from a URL, e.g.::
33 33
34 34 %install_ext https://bitbucket.org/birkenfeld/ipython-physics/raw/d1310a2ab15d/physics.py
35 35
36 36 The URL should point to an importable Python module - either a .py file
37 37 or a .zip file.
38 38
39 39 Parameters:
40 40
41 41 -n filename : Specify a name for the file, rather than taking it from
42 42 the URL.
43 43 """
44 44 opts, args = self.parse_options(parameter_s, 'n:')
45 45 try:
46 46 filename = self.shell.extension_manager.install_extension(args,
47 47 opts.get('n'))
48 48 except ValueError as e:
49 49 print e
50 50 return
51 51
52 52 filename = os.path.basename(filename)
53 53 print "Installed %s. To use it, type:" % filename
54 54 print " %%load_ext %s" % os.path.splitext(filename)[0]
55 55
56 56
57 57 @line_magic
58 58 def load_ext(self, module_str):
59 59 """Load an IPython extension by its module name."""
60 60 if not module_str:
61 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 70 @line_magic
65 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 77 if not module_str:
68 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 87 @line_magic
72 88 def reload_ext(self, module_str):
73 89 """Reload an IPython extension by its module name."""
74 90 if not module_str:
75 91 raise UsageError('Missing module name.')
76 92 self.shell.extension_manager.reload_extension(module_str)
@@ -1,289 +1,307 b''
1 1 """Implementation of magic functions related to History.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2012, IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14 from __future__ import print_function
15 15
16 16 # Stdlib
17 17 import os
18 18 from io import open as io_open
19 from IPython.external.argparse import Action
19 20
20 21 # Our own packages
21 22 from IPython.core.error import StdinNotImplementedError
22 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 26 from IPython.testing.skipdoctest import skip_doctest
24 27 from IPython.utils import io
25 28
26 29 #-----------------------------------------------------------------------------
27 30 # Magics class implementation
28 31 #-----------------------------------------------------------------------------
29 32
33
34 _unspecified = object()
35
36
30 37 @magics_class
31 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 93 @skip_doctest
34 94 @line_magic
35 95 def history(self, parameter_s = ''):
36 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 98 By default, input history is printed without line numbers so it can be
41 99 directly pasted into an editor. Use -n to show them.
42 100
43 101 By default, all input history from the current session is displayed.
44 102 Ranges of history can be indicated using the syntax:
45 103 4 : Line 4, current session
46 104 4-6 : Lines 4-6, current session
47 105 243/1-5: Lines 1-5, session 243
48 106 ~2/7 : Line 7, session 2 before current
49 107 ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line
50 108 of 6 sessions ago.
51 109 Multiple ranges can be entered, separated by spaces
52 110
53 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 113 Examples
93 114 --------
94 115 ::
95 116
96 117 In [6]: %history -n 4-6
97 118 4:a = 12
98 119 5:print a**2
99 120 6:%history -n 4-6
100 121
101 122 """
102 123
103 if not self.shell.displayhook.do_full_cache:
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')
124 args = parse_argstring(self.history, parameter_s)
108 125
109 126 # For brevity
110 127 history_manager = self.shell.history_manager
111 128
112 129 def _format_lineno(session, line):
113 130 """Helper function to format line numbers properly."""
114 131 if session in (0, history_manager.session_number):
115 132 return str(line)
116 133 return "%s/%s" % (session, line)
117 134
118 135 # Check if output to specific file was requested.
119 try:
120 outfname = opts['f']
121 except KeyError:
136 outfname = args.filename
137 if not outfname:
122 138 outfile = io.stdout # default
123 139 # We don't want to close stdout at the end!
124 140 close_at_end = False
125 141 else:
126 142 if os.path.exists(outfname):
127 143 try:
128 144 ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname)
129 145 except StdinNotImplementedError:
130 146 ans = True
131 147 if not ans:
132 148 print('Aborting.')
133 149 return
134 150 print("Overwriting file.")
135 151 outfile = io_open(outfname, 'w', encoding='utf-8')
136 152 close_at_end = True
137 153
138 print_nums = 'n' in opts
139 get_output = 'o' in opts
140 pyprompts = 'p' in opts
141 # Raw history is the default
142 raw = not('t' in opts)
154 print_nums = args.print_nums
155 get_output = args.get_output
156 pyprompts = args.pyprompts
157 raw = args.raw
143 158
144 159 pattern = None
160 limit = None if args.limit is _unspecified else args.limit
145 161
146 if 'g' in opts: # Glob search
147 pattern = "*" + args + "*" if args else "*"
148 hist = history_manager.search(pattern, raw=raw, output=get_output)
162 if args.pattern is not None:
163 if args.pattern:
164 pattern = "*" + " ".join(args.pattern) + "*"
165 else:
166 pattern = "*"
167 hist = history_manager.search(pattern, raw=raw, output=get_output,
168 n=limit)
149 169 print_nums = True
150 elif 'l' in opts: # Get 'tail'
151 try:
152 n = int(args)
153 except (ValueError, IndexError):
154 n = 10
170 elif args.limit is not _unspecified:
171 n = 10 if limit is None else limit
155 172 hist = history_manager.get_tail(n, raw=raw, output=get_output)
156 173 else:
157 if args: # Get history by ranges
158 hist = history_manager.get_range_by_str(args, raw, get_output)
174 if args.range: # Get history by ranges
175 hist = history_manager.get_range_by_str(" ".join(args.range),
176 raw, get_output)
159 177 else: # Just get history for the current session
160 178 hist = history_manager.get_range(raw=raw, output=get_output)
161 179
162 180 # We could be displaying the entire history, so let's not try to pull
163 181 # it into a list in memory. Anything that needs more space will just
164 182 # misalign.
165 183 width = 4
166 184
167 185 for session, lineno, inline in hist:
168 186 # Print user history with tabs expanded to 4 spaces. The GUI
169 187 # clients use hard tabs for easier usability in auto-indented code,
170 188 # but we want to produce PEP-8 compliant history for safe pasting
171 189 # into an editor.
172 190 if get_output:
173 191 inline, output = inline
174 192 inline = inline.expandtabs(4).rstrip()
175 193
176 194 multiline = "\n" in inline
177 195 line_sep = '\n' if multiline else ' '
178 196 if print_nums:
179 197 print(u'%s:%s' % (_format_lineno(session, lineno).rjust(width),
180 198 line_sep), file=outfile, end=u'')
181 199 if pyprompts:
182 200 print(u">>> ", end=u"", file=outfile)
183 201 if multiline:
184 202 inline = "\n... ".join(inline.splitlines()) + "\n..."
185 203 print(inline, file=outfile)
186 204 if get_output and output:
187 205 print(output, file=outfile)
188 206
189 207 if close_at_end:
190 208 outfile.close()
191 209
192 210 @line_magic
193 211 def recall(self, arg):
194 212 r"""Repeat a command, or get command to input line for editing.
195 213
196 214 %recall and %rep are equivalent.
197 215
198 216 - %recall (no arguments):
199 217
200 218 Place a string version of last computation result (stored in the
201 219 special '_' variable) to the next input prompt. Allows you to create
202 220 elaborate command lines without using copy-paste::
203 221
204 222 In[1]: l = ["hei", "vaan"]
205 223 In[2]: "".join(l)
206 224 Out[2]: heivaan
207 225 In[3]: %recall
208 226 In[4]: heivaan_ <== cursor blinking
209 227
210 228 %recall 45
211 229
212 230 Place history line 45 on the next input prompt. Use %hist to find
213 231 out the number.
214 232
215 233 %recall 1-4
216 234
217 235 Combine the specified lines into one cell, and place it on the next
218 236 input prompt. See %history for the slice syntax.
219 237
220 238 %recall foo+bar
221 239
222 240 If foo+bar can be evaluated in the user namespace, the result is
223 241 placed at the next input prompt. Otherwise, the history is searched
224 242 for lines which contain that substring, and the most recent one is
225 243 placed at the next input prompt.
226 244 """
227 245 if not arg: # Last output
228 246 self.shell.set_next_input(str(self.shell.user_ns["_"]))
229 247 return
230 248 # Get history range
231 249 histlines = self.shell.history_manager.get_range_by_str(arg)
232 250 cmd = "\n".join(x[2] for x in histlines)
233 251 if cmd:
234 252 self.shell.set_next_input(cmd.rstrip())
235 253 return
236 254
237 255 try: # Variable in user namespace
238 256 cmd = str(eval(arg, self.shell.user_ns))
239 257 except Exception: # Search for term in history
240 258 histlines = self.shell.history_manager.search("*"+arg+"*")
241 259 for h in reversed([x[2] for x in histlines]):
242 260 if 'recall' in h or 'rep' in h:
243 261 continue
244 262 self.shell.set_next_input(h.rstrip())
245 263 return
246 264 else:
247 265 self.shell.set_next_input(cmd.rstrip())
248 266 print("Couldn't evaluate or find in history:", arg)
249 267
250 268 @line_magic
251 269 def rerun(self, parameter_s=''):
252 270 """Re-run previous input
253 271
254 272 By default, you can specify ranges of input history to be repeated
255 273 (as with %history). With no arguments, it will repeat the last line.
256 274
257 275 Options:
258 276
259 277 -l <n> : Repeat the last n lines of input, not including the
260 278 current command.
261 279
262 280 -g foo : Repeat the most recent line which contains foo
263 281 """
264 282 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
265 283 if "l" in opts: # Last n lines
266 284 n = int(opts['l'])
267 285 hist = self.shell.history_manager.get_tail(n)
268 286 elif "g" in opts: # Search
269 287 p = "*"+opts['g']+"*"
270 288 hist = list(self.shell.history_manager.search(p))
271 289 for l in reversed(hist):
272 290 if "rerun" not in l[2]:
273 291 hist = [l] # The last match which isn't a %rerun
274 292 break
275 293 else:
276 294 hist = [] # No matches except %rerun
277 295 elif args: # Specify history ranges
278 296 hist = self.shell.history_manager.get_range_by_str(args)
279 297 else: # Last line
280 298 hist = self.shell.history_manager.get_tail(1)
281 299 hist = [x[2] for x in hist]
282 300 if not hist:
283 301 print("No lines in history match specification")
284 302 return
285 303 histlines = "\n".join(hist)
286 304 print("=== Executing: ===")
287 305 print(histlines)
288 306 print("=== Output: ===")
289 307 self.shell.run_cell("\n".join(hist), store_history=False)
@@ -1,703 +1,703 b''
1 1 """Implementation of namespace-related magic functions.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2012 The IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 # Stdlib
16 16 import gc
17 17 import re
18 18 import sys
19 19
20 20 # Our own packages
21 21 from IPython.core import page
22 22 from IPython.core.error import StdinNotImplementedError, UsageError
23 23 from IPython.core.magic import Magics, magics_class, line_magic
24 24 from IPython.testing.skipdoctest import skip_doctest
25 25 from IPython.utils.encoding import DEFAULT_ENCODING
26 26 from IPython.utils.openpy import read_py_file
27 27 from IPython.utils.path import get_py_filename
28 28
29 29 #-----------------------------------------------------------------------------
30 30 # Magic implementation classes
31 31 #-----------------------------------------------------------------------------
32 32
33 33 @magics_class
34 34 class NamespaceMagics(Magics):
35 35 """Magics to manage various aspects of the user's namespace.
36 36
37 37 These include listing variables, introspecting into them, etc.
38 38 """
39 39
40 40 @line_magic
41 41 def pinfo(self, parameter_s='', namespaces=None):
42 42 """Provide detailed information about an object.
43 43
44 44 '%pinfo object' is just a synonym for object? or ?object."""
45 45
46 46 #print 'pinfo par: <%s>' % parameter_s # dbg
47 47 # detail_level: 0 -> obj? , 1 -> obj??
48 48 detail_level = 0
49 49 # We need to detect if we got called as 'pinfo pinfo foo', which can
50 50 # happen if the user types 'pinfo foo?' at the cmd line.
51 51 pinfo,qmark1,oname,qmark2 = \
52 52 re.match('(pinfo )?(\?*)(.*?)(\??$)',parameter_s).groups()
53 53 if pinfo or qmark1 or qmark2:
54 54 detail_level = 1
55 55 if "*" in oname:
56 56 self.psearch(oname)
57 57 else:
58 58 self.shell._inspect('pinfo', oname, detail_level=detail_level,
59 59 namespaces=namespaces)
60 60
61 61 @line_magic
62 62 def pinfo2(self, parameter_s='', namespaces=None):
63 63 """Provide extra detailed information about an object.
64 64
65 65 '%pinfo2 object' is just a synonym for object?? or ??object."""
66 66 self.shell._inspect('pinfo', parameter_s, detail_level=1,
67 67 namespaces=namespaces)
68 68
69 69 @skip_doctest
70 70 @line_magic
71 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 74 If the object is a class, print the constructor information.
75 75
76 76 Examples
77 77 --------
78 78 ::
79 79
80 80 In [3]: %pdef urllib.urlopen
81 81 urllib.urlopen(url, data=None, proxies=None)
82 82 """
83 83 self.shell._inspect('pdef',parameter_s, namespaces)
84 84
85 85 @line_magic
86 86 def pdoc(self, parameter_s='', namespaces=None):
87 87 """Print the docstring for an object.
88 88
89 89 If the given object is a class, it will print both the class and the
90 90 constructor docstrings."""
91 91 self.shell._inspect('pdoc',parameter_s, namespaces)
92 92
93 93 @line_magic
94 94 def psource(self, parameter_s='', namespaces=None):
95 95 """Print (or run through pager) the source code for an object."""
96 96 if not parameter_s:
97 97 raise UsageError('Missing object name.')
98 98 self.shell._inspect('psource',parameter_s, namespaces)
99 99
100 100 @line_magic
101 def pfile(self, parameter_s=''):
101 def pfile(self, parameter_s='', namespaces=None):
102 102 """Print (or run through pager) the file where an object is defined.
103 103
104 104 The file opens at the line where the object definition begins. IPython
105 105 will honor the environment variable PAGER if set, and otherwise will
106 106 do its best to print the file in a convenient form.
107 107
108 108 If the given argument is not an object currently defined, IPython will
109 109 try to interpret it as a filename (automatically adding a .py extension
110 110 if needed). You can thus use %pfile as a syntax highlighting code
111 111 viewer."""
112 112
113 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 115 # if not, try the input as a filename
116 116 if out == 'not found':
117 117 try:
118 118 filename = get_py_filename(parameter_s)
119 119 except IOError as msg:
120 120 print msg
121 121 return
122 122 page.page(self.shell.pycolorize(read_py_file(filename, skip_encoding_cookie=False)))
123 123
124 124 @line_magic
125 125 def psearch(self, parameter_s=''):
126 126 """Search for object in namespaces by wildcard.
127 127
128 128 %psearch [options] PATTERN [OBJECT TYPE]
129 129
130 130 Note: ? can be used as a synonym for %psearch, at the beginning or at
131 131 the end: both a*? and ?a* are equivalent to '%psearch a*'. Still, the
132 132 rest of the command line must be unchanged (options come first), so
133 133 for example the following forms are equivalent
134 134
135 135 %psearch -i a* function
136 136 -i a* function?
137 137 ?-i a* function
138 138
139 139 Arguments:
140 140
141 141 PATTERN
142 142
143 143 where PATTERN is a string containing * as a wildcard similar to its
144 144 use in a shell. The pattern is matched in all namespaces on the
145 145 search path. By default objects starting with a single _ are not
146 146 matched, many IPython generated objects have a single
147 147 underscore. The default is case insensitive matching. Matching is
148 148 also done on the attributes of objects and not only on the objects
149 149 in a module.
150 150
151 151 [OBJECT TYPE]
152 152
153 153 Is the name of a python type from the types module. The name is
154 154 given in lowercase without the ending type, ex. StringType is
155 155 written string. By adding a type here only objects matching the
156 156 given type are matched. Using all here makes the pattern match all
157 157 types (this is the default).
158 158
159 159 Options:
160 160
161 161 -a: makes the pattern match even objects whose names start with a
162 162 single underscore. These names are normally omitted from the
163 163 search.
164 164
165 165 -i/-c: make the pattern case insensitive/sensitive. If neither of
166 166 these options are given, the default is read from your configuration
167 167 file, with the option ``InteractiveShell.wildcards_case_sensitive``.
168 168 If this option is not specified in your configuration file, IPython's
169 169 internal default is to do a case sensitive search.
170 170
171 171 -e/-s NAMESPACE: exclude/search a given namespace. The pattern you
172 172 specify can be searched in any of the following namespaces:
173 173 'builtin', 'user', 'user_global','internal', 'alias', where
174 174 'builtin' and 'user' are the search defaults. Note that you should
175 175 not use quotes when specifying namespaces.
176 176
177 177 'Builtin' contains the python module builtin, 'user' contains all
178 178 user data, 'alias' only contain the shell aliases and no python
179 179 objects, 'internal' contains objects used by IPython. The
180 180 'user_global' namespace is only used by embedded IPython instances,
181 181 and it contains module-level globals. You can add namespaces to the
182 182 search with -s or exclude them with -e (these options can be given
183 183 more than once).
184 184
185 185 Examples
186 186 --------
187 187 ::
188 188
189 189 %psearch a* -> objects beginning with an a
190 190 %psearch -e builtin a* -> objects NOT in the builtin space starting in a
191 191 %psearch a* function -> all functions beginning with an a
192 192 %psearch re.e* -> objects beginning with an e in module re
193 193 %psearch r*.e* -> objects that start with e in modules starting in r
194 194 %psearch r*.* string -> all strings in modules beginning with r
195 195
196 196 Case sensitive search::
197 197
198 198 %psearch -c a* list all object beginning with lower case a
199 199
200 200 Show objects beginning with a single _::
201 201
202 202 %psearch -a _* list objects beginning with a single underscore
203 203 """
204 204 try:
205 205 parameter_s.encode('ascii')
206 206 except UnicodeEncodeError:
207 207 print 'Python identifiers can only contain ascii characters.'
208 208 return
209 209
210 210 # default namespaces to be searched
211 211 def_search = ['user_local', 'user_global', 'builtin']
212 212
213 213 # Process options/args
214 214 opts,args = self.parse_options(parameter_s,'cias:e:',list_all=True)
215 215 opt = opts.get
216 216 shell = self.shell
217 217 psearch = shell.inspector.psearch
218 218
219 219 # select case options
220 220 if 'i' in opts:
221 221 ignore_case = True
222 222 elif 'c' in opts:
223 223 ignore_case = False
224 224 else:
225 225 ignore_case = not shell.wildcards_case_sensitive
226 226
227 227 # Build list of namespaces to search from user options
228 228 def_search.extend(opt('s',[]))
229 229 ns_exclude = ns_exclude=opt('e',[])
230 230 ns_search = [nm for nm in def_search if nm not in ns_exclude]
231 231
232 232 # Call the actual search
233 233 try:
234 234 psearch(args,shell.ns_table,ns_search,
235 235 show_all=opt('a'),ignore_case=ignore_case)
236 236 except:
237 237 shell.showtraceback()
238 238
239 239 @skip_doctest
240 240 @line_magic
241 241 def who_ls(self, parameter_s=''):
242 242 """Return a sorted list of all interactive variables.
243 243
244 244 If arguments are given, only variables of types matching these
245 245 arguments are returned.
246 246
247 247 Examples
248 248 --------
249 249
250 250 Define two variables and list them with who_ls::
251 251
252 252 In [1]: alpha = 123
253 253
254 254 In [2]: beta = 'test'
255 255
256 256 In [3]: %who_ls
257 257 Out[3]: ['alpha', 'beta']
258 258
259 259 In [4]: %who_ls int
260 260 Out[4]: ['alpha']
261 261
262 262 In [5]: %who_ls str
263 263 Out[5]: ['beta']
264 264 """
265 265
266 266 user_ns = self.shell.user_ns
267 267 user_ns_hidden = self.shell.user_ns_hidden
268 268 out = [ i for i in user_ns
269 269 if not i.startswith('_') \
270 270 and not i in user_ns_hidden ]
271 271
272 272 typelist = parameter_s.split()
273 273 if typelist:
274 274 typeset = set(typelist)
275 275 out = [i for i in out if type(user_ns[i]).__name__ in typeset]
276 276
277 277 out.sort()
278 278 return out
279 279
280 280 @skip_doctest
281 281 @line_magic
282 282 def who(self, parameter_s=''):
283 283 """Print all interactive variables, with some minimal formatting.
284 284
285 285 If any arguments are given, only variables whose type matches one of
286 286 these are printed. For example::
287 287
288 288 %who function str
289 289
290 290 will only list functions and strings, excluding all other types of
291 291 variables. To find the proper type names, simply use type(var) at a
292 292 command line to see how python prints type names. For example:
293 293
294 294 ::
295 295
296 296 In [1]: type('hello')\\
297 297 Out[1]: <type 'str'>
298 298
299 299 indicates that the type name for strings is 'str'.
300 300
301 301 ``%who`` always excludes executed names loaded through your configuration
302 302 file and things which are internal to IPython.
303 303
304 304 This is deliberate, as typically you may load many modules and the
305 305 purpose of %who is to show you only what you've manually defined.
306 306
307 307 Examples
308 308 --------
309 309
310 310 Define two variables and list them with who::
311 311
312 312 In [1]: alpha = 123
313 313
314 314 In [2]: beta = 'test'
315 315
316 316 In [3]: %who
317 317 alpha beta
318 318
319 319 In [4]: %who int
320 320 alpha
321 321
322 322 In [5]: %who str
323 323 beta
324 324 """
325 325
326 326 varlist = self.who_ls(parameter_s)
327 327 if not varlist:
328 328 if parameter_s:
329 329 print 'No variables match your requested type.'
330 330 else:
331 331 print 'Interactive namespace is empty.'
332 332 return
333 333
334 334 # if we have variables, move on...
335 335 count = 0
336 336 for i in varlist:
337 337 print i+'\t',
338 338 count += 1
339 339 if count > 8:
340 340 count = 0
341 341 print
342 342 print
343 343
344 344 @skip_doctest
345 345 @line_magic
346 346 def whos(self, parameter_s=''):
347 347 """Like %who, but gives some extra information about each variable.
348 348
349 349 The same type filtering of %who can be applied here.
350 350
351 351 For all variables, the type is printed. Additionally it prints:
352 352
353 353 - For {},[],(): their length.
354 354
355 355 - For numpy arrays, a summary with shape, number of
356 356 elements, typecode and size in memory.
357 357
358 358 - Everything else: a string representation, snipping their middle if
359 359 too long.
360 360
361 361 Examples
362 362 --------
363 363
364 364 Define two variables and list them with whos::
365 365
366 366 In [1]: alpha = 123
367 367
368 368 In [2]: beta = 'test'
369 369
370 370 In [3]: %whos
371 371 Variable Type Data/Info
372 372 --------------------------------
373 373 alpha int 123
374 374 beta str test
375 375 """
376 376
377 377 varnames = self.who_ls(parameter_s)
378 378 if not varnames:
379 379 if parameter_s:
380 380 print 'No variables match your requested type.'
381 381 else:
382 382 print 'Interactive namespace is empty.'
383 383 return
384 384
385 385 # if we have variables, move on...
386 386
387 387 # for these types, show len() instead of data:
388 388 seq_types = ['dict', 'list', 'tuple']
389 389
390 390 # for numpy arrays, display summary info
391 391 ndarray_type = None
392 392 if 'numpy' in sys.modules:
393 393 try:
394 394 from numpy import ndarray
395 395 except ImportError:
396 396 pass
397 397 else:
398 398 ndarray_type = ndarray.__name__
399 399
400 400 # Find all variable names and types so we can figure out column sizes
401 401 def get_vars(i):
402 402 return self.shell.user_ns[i]
403 403
404 404 # some types are well known and can be shorter
405 405 abbrevs = {'IPython.core.macro.Macro' : 'Macro'}
406 406 def type_name(v):
407 407 tn = type(v).__name__
408 408 return abbrevs.get(tn,tn)
409 409
410 410 varlist = map(get_vars,varnames)
411 411
412 412 typelist = []
413 413 for vv in varlist:
414 414 tt = type_name(vv)
415 415
416 416 if tt=='instance':
417 417 typelist.append( abbrevs.get(str(vv.__class__),
418 418 str(vv.__class__)))
419 419 else:
420 420 typelist.append(tt)
421 421
422 422 # column labels and # of spaces as separator
423 423 varlabel = 'Variable'
424 424 typelabel = 'Type'
425 425 datalabel = 'Data/Info'
426 426 colsep = 3
427 427 # variable format strings
428 428 vformat = "{0:<{varwidth}}{1:<{typewidth}}"
429 429 aformat = "%s: %s elems, type `%s`, %s bytes"
430 430 # find the size of the columns to format the output nicely
431 431 varwidth = max(max(map(len,varnames)), len(varlabel)) + colsep
432 432 typewidth = max(max(map(len,typelist)), len(typelabel)) + colsep
433 433 # table header
434 434 print varlabel.ljust(varwidth) + typelabel.ljust(typewidth) + \
435 435 ' '+datalabel+'\n' + '-'*(varwidth+typewidth+len(datalabel)+1)
436 436 # and the table itself
437 437 kb = 1024
438 438 Mb = 1048576 # kb**2
439 439 for vname,var,vtype in zip(varnames,varlist,typelist):
440 440 print vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth),
441 441 if vtype in seq_types:
442 442 print "n="+str(len(var))
443 443 elif vtype == ndarray_type:
444 444 vshape = str(var.shape).replace(',','').replace(' ','x')[1:-1]
445 445 if vtype==ndarray_type:
446 446 # numpy
447 447 vsize = var.size
448 448 vbytes = vsize*var.itemsize
449 449 vdtype = var.dtype
450 450
451 451 if vbytes < 100000:
452 452 print aformat % (vshape, vsize, vdtype, vbytes)
453 453 else:
454 454 print aformat % (vshape, vsize, vdtype, vbytes),
455 455 if vbytes < Mb:
456 456 print '(%s kb)' % (vbytes/kb,)
457 457 else:
458 458 print '(%s Mb)' % (vbytes/Mb,)
459 459 else:
460 460 try:
461 461 vstr = str(var)
462 462 except UnicodeEncodeError:
463 463 vstr = unicode(var).encode(DEFAULT_ENCODING,
464 464 'backslashreplace')
465 465 except:
466 466 vstr = "<object with id %d (str() failed)>" % id(var)
467 467 vstr = vstr.replace('\n', '\\n')
468 468 if len(vstr) < 50:
469 469 print vstr
470 470 else:
471 471 print vstr[:25] + "<...>" + vstr[-25:]
472 472
473 473 @line_magic
474 474 def reset(self, parameter_s=''):
475 475 """Resets the namespace by removing all names defined by the user, if
476 476 called without arguments, or by removing some types of objects, such
477 477 as everything currently in IPython's In[] and Out[] containers (see
478 478 the parameters for details).
479 479
480 480 Parameters
481 481 ----------
482 482 -f : force reset without asking for confirmation.
483 483
484 484 -s : 'Soft' reset: Only clears your namespace, leaving history intact.
485 485 References to objects may be kept. By default (without this option),
486 486 we do a 'hard' reset, giving you a new session and removing all
487 487 references to objects from the current session.
488 488
489 489 in : reset input history
490 490
491 491 out : reset output history
492 492
493 493 dhist : reset directory history
494 494
495 495 array : reset only variables that are NumPy arrays
496 496
497 497 See Also
498 498 --------
499 499 magic_reset_selective : invoked as ``%reset_selective``
500 500
501 501 Examples
502 502 --------
503 503 ::
504 504
505 505 In [6]: a = 1
506 506
507 507 In [7]: a
508 508 Out[7]: 1
509 509
510 510 In [8]: 'a' in _ip.user_ns
511 511 Out[8]: True
512 512
513 513 In [9]: %reset -f
514 514
515 515 In [1]: 'a' in _ip.user_ns
516 516 Out[1]: False
517 517
518 518 In [2]: %reset -f in
519 519 Flushing input history
520 520
521 521 In [3]: %reset -f dhist in
522 522 Flushing directory history
523 523 Flushing input history
524 524
525 525 Notes
526 526 -----
527 527 Calling this magic from clients that do not implement standard input,
528 528 such as the ipython notebook interface, will reset the namespace
529 529 without confirmation.
530 530 """
531 531 opts, args = self.parse_options(parameter_s,'sf', mode='list')
532 532 if 'f' in opts:
533 533 ans = True
534 534 else:
535 535 try:
536 536 ans = self.shell.ask_yes_no(
537 537 "Once deleted, variables cannot be recovered. Proceed (y/[n])?",
538 538 default='n')
539 539 except StdinNotImplementedError:
540 540 ans = True
541 541 if not ans:
542 542 print 'Nothing done.'
543 543 return
544 544
545 545 if 's' in opts: # Soft reset
546 546 user_ns = self.shell.user_ns
547 547 for i in self.who_ls():
548 548 del(user_ns[i])
549 549 elif len(args) == 0: # Hard reset
550 550 self.shell.reset(new_session = False)
551 551
552 552 # reset in/out/dhist/array: previously extensinions/clearcmd.py
553 553 ip = self.shell
554 554 user_ns = self.shell.user_ns # local lookup, heavily used
555 555
556 556 for target in args:
557 557 target = target.lower() # make matches case insensitive
558 558 if target == 'out':
559 559 print "Flushing output cache (%d entries)" % len(user_ns['_oh'])
560 560 self.shell.displayhook.flush()
561 561
562 562 elif target == 'in':
563 563 print "Flushing input history"
564 564 pc = self.shell.displayhook.prompt_count + 1
565 565 for n in range(1, pc):
566 566 key = '_i'+repr(n)
567 567 user_ns.pop(key,None)
568 568 user_ns.update(dict(_i=u'',_ii=u'',_iii=u''))
569 569 hm = ip.history_manager
570 570 # don't delete these, as %save and %macro depending on the
571 571 # length of these lists to be preserved
572 572 hm.input_hist_parsed[:] = [''] * pc
573 573 hm.input_hist_raw[:] = [''] * pc
574 574 # hm has internal machinery for _i,_ii,_iii, clear it out
575 575 hm._i = hm._ii = hm._iii = hm._i00 = u''
576 576
577 577 elif target == 'array':
578 578 # Support cleaning up numpy arrays
579 579 try:
580 580 from numpy import ndarray
581 581 # This must be done with items and not iteritems because
582 582 # we're going to modify the dict in-place.
583 583 for x,val in user_ns.items():
584 584 if isinstance(val,ndarray):
585 585 del user_ns[x]
586 586 except ImportError:
587 587 print "reset array only works if Numpy is available."
588 588
589 589 elif target == 'dhist':
590 590 print "Flushing directory history"
591 591 del user_ns['_dh'][:]
592 592
593 593 else:
594 594 print "Don't know how to reset ",
595 595 print target + ", please run `%reset?` for details"
596 596
597 597 gc.collect()
598 598
599 599 @line_magic
600 600 def reset_selective(self, parameter_s=''):
601 601 """Resets the namespace by removing names defined by the user.
602 602
603 603 Input/Output history are left around in case you need them.
604 604
605 605 %reset_selective [-f] regex
606 606
607 607 No action is taken if regex is not included
608 608
609 609 Options
610 610 -f : force reset without asking for confirmation.
611 611
612 612 See Also
613 613 --------
614 614 magic_reset : invoked as ``%reset``
615 615
616 616 Examples
617 617 --------
618 618
619 619 We first fully reset the namespace so your output looks identical to
620 620 this example for pedagogical reasons; in practice you do not need a
621 621 full reset::
622 622
623 623 In [1]: %reset -f
624 624
625 625 Now, with a clean namespace we can make a few variables and use
626 626 ``%reset_selective`` to only delete names that match our regexp::
627 627
628 628 In [2]: a=1; b=2; c=3; b1m=4; b2m=5; b3m=6; b4m=7; b2s=8
629 629
630 630 In [3]: who_ls
631 631 Out[3]: ['a', 'b', 'b1m', 'b2m', 'b2s', 'b3m', 'b4m', 'c']
632 632
633 633 In [4]: %reset_selective -f b[2-3]m
634 634
635 635 In [5]: who_ls
636 636 Out[5]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c']
637 637
638 638 In [6]: %reset_selective -f d
639 639
640 640 In [7]: who_ls
641 641 Out[7]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c']
642 642
643 643 In [8]: %reset_selective -f c
644 644
645 645 In [9]: who_ls
646 646 Out[9]: ['a', 'b', 'b1m', 'b2s', 'b4m']
647 647
648 648 In [10]: %reset_selective -f b
649 649
650 650 In [11]: who_ls
651 651 Out[11]: ['a']
652 652
653 653 Notes
654 654 -----
655 655 Calling this magic from clients that do not implement standard input,
656 656 such as the ipython notebook interface, will reset the namespace
657 657 without confirmation.
658 658 """
659 659
660 660 opts, regex = self.parse_options(parameter_s,'f')
661 661
662 662 if 'f' in opts:
663 663 ans = True
664 664 else:
665 665 try:
666 666 ans = self.shell.ask_yes_no(
667 667 "Once deleted, variables cannot be recovered. Proceed (y/[n])? ",
668 668 default='n')
669 669 except StdinNotImplementedError:
670 670 ans = True
671 671 if not ans:
672 672 print 'Nothing done.'
673 673 return
674 674 user_ns = self.shell.user_ns
675 675 if not regex:
676 676 print 'No regex pattern specified. Nothing done.'
677 677 return
678 678 else:
679 679 try:
680 680 m = re.compile(regex)
681 681 except TypeError:
682 682 raise TypeError('regex must be a string or compiled pattern')
683 683 for i in self.who_ls():
684 684 if m.search(i):
685 685 del(user_ns[i])
686 686
687 687 @line_magic
688 688 def xdel(self, parameter_s=''):
689 689 """Delete a variable, trying to clear it from anywhere that
690 690 IPython's machinery has references to it. By default, this uses
691 691 the identity of the named object in the user namespace to remove
692 692 references held under other names. The object is also removed
693 693 from the output history.
694 694
695 695 Options
696 696 -n : Delete the specified name from all namespaces, without
697 697 checking their identity.
698 698 """
699 699 opts, varname = self.parse_options(parameter_s,'n')
700 700 try:
701 701 self.shell.del_var(varname, ('n' in opts))
702 702 except (NameError, ValueError) as e:
703 703 print type(e).__name__ +": "+ str(e)
@@ -1,873 +1,873 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tools for inspecting Python objects.
3 3
4 4 Uses syntax highlighting for presenting the various information elements.
5 5
6 6 Similar in spirit to the inspect module, but all calls take a name argument to
7 7 reference the name under which an object is being read.
8 8 """
9 9
10 10 #*****************************************************************************
11 11 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
12 12 #
13 13 # Distributed under the terms of the BSD License. The full license is in
14 14 # the file COPYING, distributed as part of this software.
15 15 #*****************************************************************************
16 16 from __future__ import print_function
17 17
18 18 __all__ = ['Inspector','InspectColors']
19 19
20 20 # stdlib modules
21 21 import __builtin__
22 22 import inspect
23 23 import linecache
24 24 import os
25 25 import sys
26 26 import types
27 27 import io as stdlib_io
28 28
29 29 from collections import namedtuple
30 30 try:
31 31 from itertools import izip_longest
32 32 except ImportError:
33 33 from itertools import zip_longest as izip_longest
34 34
35 35 # IPython's own
36 36 from IPython.core import page
37 37 from IPython.testing.skipdoctest import skip_doctest_py3
38 38 from IPython.utils import PyColorize
39 39 from IPython.utils import io
40 40 from IPython.utils import openpy
41 41 from IPython.utils import py3compat
42 42 from IPython.utils.text import indent
43 43 from IPython.utils.wildcard import list_namespace
44 44 from IPython.utils.coloransi import *
45 45 from IPython.utils.py3compat import cast_unicode
46 46
47 47 #****************************************************************************
48 48 # Builtin color schemes
49 49
50 50 Colors = TermColors # just a shorthand
51 51
52 52 # Build a few color schemes
53 53 NoColor = ColorScheme(
54 54 'NoColor',{
55 55 'header' : Colors.NoColor,
56 56 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
57 57 } )
58 58
59 59 LinuxColors = ColorScheme(
60 60 'Linux',{
61 61 'header' : Colors.LightRed,
62 62 'normal' : Colors.Normal # color off (usu. Colors.Normal)
63 63 } )
64 64
65 65 LightBGColors = ColorScheme(
66 66 'LightBG',{
67 67 'header' : Colors.Red,
68 68 'normal' : Colors.Normal # color off (usu. Colors.Normal)
69 69 } )
70 70
71 71 # Build table of color schemes (needed by the parser)
72 72 InspectColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
73 73 'Linux')
74 74
75 75 #****************************************************************************
76 76 # Auxiliary functions and objects
77 77
78 78 # See the messaging spec for the definition of all these fields. This list
79 79 # effectively defines the order of display
80 80 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
81 81 'length', 'file', 'definition', 'docstring', 'source',
82 82 'init_definition', 'class_docstring', 'init_docstring',
83 83 'call_def', 'call_docstring',
84 84 # These won't be printed but will be used to determine how to
85 85 # format the object
86 86 'ismagic', 'isalias', 'isclass', 'argspec', 'found', 'name'
87 87 ]
88 88
89 89
90 90 def object_info(**kw):
91 91 """Make an object info dict with all fields present."""
92 92 infodict = dict(izip_longest(info_fields, [None]))
93 93 infodict.update(kw)
94 94 return infodict
95 95
96 96
97 97 def get_encoding(obj):
98 98 """Get encoding for python source file defining obj
99 99
100 100 Returns None if obj is not defined in a sourcefile.
101 101 """
102 102 ofile = find_file(obj)
103 103 # run contents of file through pager starting at line where the object
104 104 # is defined, as long as the file isn't binary and is actually on the
105 105 # filesystem.
106 106 if ofile is None:
107 107 return None
108 108 elif ofile.endswith(('.so', '.dll', '.pyd')):
109 109 return None
110 110 elif not os.path.isfile(ofile):
111 111 return None
112 112 else:
113 113 # Print only text files, not extension binaries. Note that
114 114 # getsourcelines returns lineno with 1-offset and page() uses
115 115 # 0-offset, so we must adjust.
116 116 buffer = stdlib_io.open(ofile, 'rb') # Tweaked to use io.open for Python 2
117 117 encoding, lines = openpy.detect_encoding(buffer.readline)
118 118 return encoding
119 119
120 120 def getdoc(obj):
121 121 """Stable wrapper around inspect.getdoc.
122 122
123 123 This can't crash because of attribute problems.
124 124
125 125 It also attempts to call a getdoc() method on the given object. This
126 126 allows objects which provide their docstrings via non-standard mechanisms
127 127 (like Pyro proxies) to still be inspected by ipython's ? system."""
128 128 # Allow objects to offer customized documentation via a getdoc method:
129 129 try:
130 130 ds = obj.getdoc()
131 131 except Exception:
132 132 pass
133 133 else:
134 134 # if we get extra info, we add it to the normal docstring.
135 135 if isinstance(ds, basestring):
136 136 return inspect.cleandoc(ds)
137 137
138 138 try:
139 139 docstr = inspect.getdoc(obj)
140 140 encoding = get_encoding(obj)
141 141 return py3compat.cast_unicode(docstr, encoding=encoding)
142 142 except Exception:
143 143 # Harden against an inspect failure, which can occur with
144 144 # SWIG-wrapped extensions.
145 145 raise
146 146 return None
147 147
148 148
149 149 def getsource(obj,is_binary=False):
150 150 """Wrapper around inspect.getsource.
151 151
152 152 This can be modified by other projects to provide customized source
153 153 extraction.
154 154
155 155 Inputs:
156 156
157 157 - obj: an object whose source code we will attempt to extract.
158 158
159 159 Optional inputs:
160 160
161 161 - is_binary: whether the object is known to come from a binary source.
162 162 This implementation will skip returning any output for binary objects, but
163 163 custom extractors may know how to meaningfully process them."""
164 164
165 165 if is_binary:
166 166 return None
167 167 else:
168 168 # get source if obj was decorated with @decorator
169 169 if hasattr(obj,"__wrapped__"):
170 170 obj = obj.__wrapped__
171 171 try:
172 172 src = inspect.getsource(obj)
173 173 except TypeError:
174 174 if hasattr(obj,'__class__'):
175 175 src = inspect.getsource(obj.__class__)
176 176 encoding = get_encoding(obj)
177 177 return cast_unicode(src, encoding=encoding)
178 178
179 179 def getargspec(obj):
180 180 """Get the names and default values of a function's arguments.
181 181
182 182 A tuple of four things is returned: (args, varargs, varkw, defaults).
183 183 'args' is a list of the argument names (it may contain nested lists).
184 184 'varargs' and 'varkw' are the names of the * and ** arguments or None.
185 185 'defaults' is an n-tuple of the default values of the last n arguments.
186 186
187 187 Modified version of inspect.getargspec from the Python Standard
188 188 Library."""
189 189
190 190 if inspect.isfunction(obj):
191 191 func_obj = obj
192 192 elif inspect.ismethod(obj):
193 193 func_obj = obj.im_func
194 194 elif hasattr(obj, '__call__'):
195 195 func_obj = obj.__call__
196 196 else:
197 197 raise TypeError('arg is not a Python function')
198 198 args, varargs, varkw = inspect.getargs(func_obj.func_code)
199 199 return args, varargs, varkw, func_obj.func_defaults
200 200
201 201
202 202 def format_argspec(argspec):
203 203 """Format argspect, convenience wrapper around inspect's.
204 204
205 205 This takes a dict instead of ordered arguments and calls
206 206 inspect.format_argspec with the arguments in the necessary order.
207 207 """
208 208 return inspect.formatargspec(argspec['args'], argspec['varargs'],
209 209 argspec['varkw'], argspec['defaults'])
210 210
211 211
212 212 def call_tip(oinfo, format_call=True):
213 213 """Extract call tip data from an oinfo dict.
214 214
215 215 Parameters
216 216 ----------
217 217 oinfo : dict
218 218
219 219 format_call : bool, optional
220 220 If True, the call line is formatted and returned as a string. If not, a
221 221 tuple of (name, argspec) is returned.
222 222
223 223 Returns
224 224 -------
225 225 call_info : None, str or (str, dict) tuple.
226 226 When format_call is True, the whole call information is formattted as a
227 227 single string. Otherwise, the object's name and its argspec dict are
228 228 returned. If no call information is available, None is returned.
229 229
230 230 docstring : str or None
231 231 The most relevant docstring for calling purposes is returned, if
232 232 available. The priority is: call docstring for callable instances, then
233 233 constructor docstring for classes, then main object's docstring otherwise
234 234 (regular functions).
235 235 """
236 236 # Get call definition
237 237 argspec = oinfo.get('argspec')
238 238 if argspec is None:
239 239 call_line = None
240 240 else:
241 241 # Callable objects will have 'self' as their first argument, prune
242 242 # it out if it's there for clarity (since users do *not* pass an
243 243 # extra first argument explicitly).
244 244 try:
245 245 has_self = argspec['args'][0] == 'self'
246 246 except (KeyError, IndexError):
247 247 pass
248 248 else:
249 249 if has_self:
250 250 argspec['args'] = argspec['args'][1:]
251 251
252 252 call_line = oinfo['name']+format_argspec(argspec)
253 253
254 254 # Now get docstring.
255 255 # The priority is: call docstring, constructor docstring, main one.
256 256 doc = oinfo.get('call_docstring')
257 257 if doc is None:
258 258 doc = oinfo.get('init_docstring')
259 259 if doc is None:
260 260 doc = oinfo.get('docstring','')
261 261
262 262 return call_line, doc
263 263
264 264
265 265 def find_file(obj):
266 266 """Find the absolute path to the file where an object was defined.
267 267
268 268 This is essentially a robust wrapper around `inspect.getabsfile`.
269 269
270 270 Returns None if no file can be found.
271 271
272 272 Parameters
273 273 ----------
274 274 obj : any Python object
275 275
276 276 Returns
277 277 -------
278 278 fname : str
279 279 The absolute path to the file where the object was defined.
280 280 """
281 281 # get source if obj was decorated with @decorator
282 282 if hasattr(obj, '__wrapped__'):
283 283 obj = obj.__wrapped__
284 284
285 285 fname = None
286 286 try:
287 287 fname = inspect.getabsfile(obj)
288 288 except TypeError:
289 289 # For an instance, the file that matters is where its class was
290 290 # declared.
291 291 if hasattr(obj, '__class__'):
292 292 try:
293 293 fname = inspect.getabsfile(obj.__class__)
294 294 except TypeError:
295 295 # Can happen for builtins
296 296 pass
297 297 except:
298 298 pass
299 299 return fname
300 300
301 301
302 302 def find_source_lines(obj):
303 303 """Find the line number in a file where an object was defined.
304 304
305 305 This is essentially a robust wrapper around `inspect.getsourcelines`.
306 306
307 307 Returns None if no file can be found.
308 308
309 309 Parameters
310 310 ----------
311 311 obj : any Python object
312 312
313 313 Returns
314 314 -------
315 315 lineno : int
316 316 The line number where the object definition starts.
317 317 """
318 318 # get source if obj was decorated with @decorator
319 319 if hasattr(obj, '__wrapped__'):
320 320 obj = obj.__wrapped__
321 321
322 322 try:
323 323 try:
324 324 lineno = inspect.getsourcelines(obj)[1]
325 325 except TypeError:
326 326 # For instances, try the class object like getsource() does
327 327 if hasattr(obj, '__class__'):
328 328 lineno = inspect.getsourcelines(obj.__class__)[1]
329 329 except:
330 330 return None
331 331
332 332 return lineno
333 333
334 334
335 335 class Inspector:
336 336 def __init__(self, color_table=InspectColors,
337 337 code_color_table=PyColorize.ANSICodeColors,
338 338 scheme='NoColor',
339 339 str_detail_level=0):
340 340 self.color_table = color_table
341 341 self.parser = PyColorize.Parser(code_color_table,out='str')
342 342 self.format = self.parser.format
343 343 self.str_detail_level = str_detail_level
344 344 self.set_active_scheme(scheme)
345 345
346 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 349 If any exception is generated, None is returned instead and the
350 350 exception is suppressed."""
351 351
352 352 try:
353 353 hdef = oname + inspect.formatargspec(*getargspec(obj))
354 354 return cast_unicode(hdef)
355 355 except:
356 356 return None
357 357
358 358 def __head(self,h):
359 359 """Return a header string with proper colors."""
360 360 return '%s%s%s' % (self.color_table.active_colors.header,h,
361 361 self.color_table.active_colors.normal)
362 362
363 363 def set_active_scheme(self, scheme):
364 364 self.color_table.set_active_scheme(scheme)
365 365 self.parser.color_table.set_active_scheme(scheme)
366 366
367 367 def noinfo(self, msg, oname):
368 368 """Generic message when no information is found."""
369 369 print('No %s found' % msg, end=' ')
370 370 if oname:
371 371 print('for %s' % oname)
372 372 else:
373 373 print()
374 374
375 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 378 If the object is a class, print the constructor information."""
379 379
380 380 if not callable(obj):
381 381 print('Object is not callable.')
382 382 return
383 383
384 384 header = ''
385 385
386 386 if inspect.isclass(obj):
387 387 header = self.__head('Class constructor information:\n')
388 388 obj = obj.__init__
389 389 elif (not py3compat.PY3) and type(obj) is types.InstanceType:
390 390 obj = obj.__call__
391 391
392 392 output = self._getdef(obj,oname)
393 393 if output is None:
394 394 self.noinfo('definition header',oname)
395 395 else:
396 396 print(header,self.format(output), end=' ', file=io.stdout)
397 397
398 398 # In Python 3, all classes are new-style, so they all have __init__.
399 399 @skip_doctest_py3
400 400 def pdoc(self,obj,oname='',formatter = None):
401 401 """Print the docstring for any object.
402 402
403 403 Optional:
404 404 -formatter: a function to run the docstring through for specially
405 405 formatted docstrings.
406 406
407 407 Examples
408 408 --------
409 409
410 410 In [1]: class NoInit:
411 411 ...: pass
412 412
413 413 In [2]: class NoDoc:
414 414 ...: def __init__(self):
415 415 ...: pass
416 416
417 417 In [3]: %pdoc NoDoc
418 418 No documentation found for NoDoc
419 419
420 420 In [4]: %pdoc NoInit
421 421 No documentation found for NoInit
422 422
423 423 In [5]: obj = NoInit()
424 424
425 425 In [6]: %pdoc obj
426 426 No documentation found for obj
427 427
428 428 In [5]: obj2 = NoDoc()
429 429
430 430 In [6]: %pdoc obj2
431 431 No documentation found for obj2
432 432 """
433 433
434 434 head = self.__head # For convenience
435 435 lines = []
436 436 ds = getdoc(obj)
437 437 if formatter:
438 438 ds = formatter(ds)
439 439 if ds:
440 440 lines.append(head("Class Docstring:"))
441 441 lines.append(indent(ds))
442 442 if inspect.isclass(obj) and hasattr(obj, '__init__'):
443 443 init_ds = getdoc(obj.__init__)
444 444 if init_ds is not None:
445 445 lines.append(head("Constructor Docstring:"))
446 446 lines.append(indent(init_ds))
447 447 elif hasattr(obj,'__call__'):
448 448 call_ds = getdoc(obj.__call__)
449 449 if call_ds:
450 450 lines.append(head("Calling Docstring:"))
451 451 lines.append(indent(call_ds))
452 452
453 453 if not lines:
454 454 self.noinfo('documentation',oname)
455 455 else:
456 456 page.page('\n'.join(lines))
457 457
458 458 def psource(self,obj,oname=''):
459 459 """Print the source code for an object."""
460 460
461 461 # Flush the source cache because inspect can return out-of-date source
462 462 linecache.checkcache()
463 463 try:
464 464 src = getsource(obj)
465 465 except:
466 466 self.noinfo('source',oname)
467 467 else:
468 468 page.page(self.format(src))
469 469
470 470 def pfile(self, obj, oname=''):
471 471 """Show the whole file where an object was defined."""
472 472
473 473 lineno = find_source_lines(obj)
474 474 if lineno is None:
475 475 self.noinfo('file', oname)
476 476 return
477 477
478 478 ofile = find_file(obj)
479 479 # run contents of file through pager starting at line where the object
480 480 # is defined, as long as the file isn't binary and is actually on the
481 481 # filesystem.
482 482 if ofile.endswith(('.so', '.dll', '.pyd')):
483 483 print('File %r is binary, not printing.' % ofile)
484 484 elif not os.path.isfile(ofile):
485 485 print('File %r does not exist, not printing.' % ofile)
486 486 else:
487 487 # Print only text files, not extension binaries. Note that
488 488 # getsourcelines returns lineno with 1-offset and page() uses
489 489 # 0-offset, so we must adjust.
490 490 page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1)
491 491
492 492 def _format_fields(self, fields, title_width=12):
493 493 """Formats a list of fields for display.
494 494
495 495 Parameters
496 496 ----------
497 497 fields : list
498 498 A list of 2-tuples: (field_title, field_content)
499 499 title_width : int
500 500 How many characters to pad titles to. Default 12.
501 501 """
502 502 out = []
503 503 header = self.__head
504 504 for title, content in fields:
505 505 if len(content.splitlines()) > 1:
506 506 title = header(title + ":") + "\n"
507 507 else:
508 508 title = header((title+":").ljust(title_width))
509 509 out.append(cast_unicode(title) + cast_unicode(content))
510 510 return "\n".join(out)
511 511
512 512 # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict)
513 513 pinfo_fields1 = [("Type", "type_name"),
514 514 ]
515 515
516 516 pinfo_fields2 = [("String Form", "string_form"),
517 517 ]
518 518
519 519 pinfo_fields3 = [("Length", "length"),
520 520 ("File", "file"),
521 521 ("Definition", "definition"),
522 522 ]
523 523
524 524 pinfo_fields_obj = [("Class Docstring", "class_docstring"),
525 525 ("Constructor Docstring","init_docstring"),
526 526 ("Call def", "call_def"),
527 527 ("Call docstring", "call_docstring")]
528 528
529 529 def pinfo(self,obj,oname='',formatter=None,info=None,detail_level=0):
530 530 """Show detailed information about an object.
531 531
532 532 Optional arguments:
533 533
534 534 - oname: name of the variable pointing to the object.
535 535
536 536 - formatter: special formatter for docstrings (see pdoc)
537 537
538 538 - info: a structure with some information fields which may have been
539 539 precomputed already.
540 540
541 541 - detail_level: if set to 1, more information is given.
542 542 """
543 543 info = self.info(obj, oname=oname, formatter=formatter,
544 544 info=info, detail_level=detail_level)
545 545 displayfields = []
546 546 def add_fields(fields):
547 547 for title, key in fields:
548 548 field = info[key]
549 549 if field is not None:
550 550 displayfields.append((title, field.rstrip()))
551 551
552 552 add_fields(self.pinfo_fields1)
553 553
554 554 # Base class for old-style instances
555 555 if (not py3compat.PY3) and isinstance(obj, types.InstanceType) and info['base_class']:
556 556 displayfields.append(("Base Class", info['base_class'].rstrip()))
557 557
558 558 add_fields(self.pinfo_fields2)
559 559
560 560 # Namespace
561 561 if info['namespace'] != 'Interactive':
562 562 displayfields.append(("Namespace", info['namespace'].rstrip()))
563 563
564 564 add_fields(self.pinfo_fields3)
565 565
566 566 # Source or docstring, depending on detail level and whether
567 567 # source found.
568 568 if detail_level > 0 and info['source'] is not None:
569 569 displayfields.append(("Source",
570 570 self.format(cast_unicode(info['source']))))
571 571 elif info['docstring'] is not None:
572 572 displayfields.append(("Docstring", info["docstring"]))
573 573
574 574 # Constructor info for classes
575 575 if info['isclass']:
576 576 if info['init_definition'] or info['init_docstring']:
577 577 displayfields.append(("Constructor information", ""))
578 578 if info['init_definition'] is not None:
579 579 displayfields.append((" Definition",
580 580 info['init_definition'].rstrip()))
581 581 if info['init_docstring'] is not None:
582 582 displayfields.append((" Docstring",
583 583 indent(info['init_docstring'])))
584 584
585 585 # Info for objects:
586 586 else:
587 587 add_fields(self.pinfo_fields_obj)
588 588
589 589 # Finally send to printer/pager:
590 590 if displayfields:
591 591 page.page(self._format_fields(displayfields))
592 592
593 593 def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
594 594 """Compute a dict with detailed information about an object.
595 595
596 596 Optional arguments:
597 597
598 598 - oname: name of the variable pointing to the object.
599 599
600 600 - formatter: special formatter for docstrings (see pdoc)
601 601
602 602 - info: a structure with some information fields which may have been
603 603 precomputed already.
604 604
605 605 - detail_level: if set to 1, more information is given.
606 606 """
607 607
608 608 obj_type = type(obj)
609 609
610 610 header = self.__head
611 611 if info is None:
612 612 ismagic = 0
613 613 isalias = 0
614 614 ospace = ''
615 615 else:
616 616 ismagic = info.ismagic
617 617 isalias = info.isalias
618 618 ospace = info.namespace
619 619
620 620 # Get docstring, special-casing aliases:
621 621 if isalias:
622 622 if not callable(obj):
623 623 try:
624 624 ds = "Alias to the system command:\n %s" % obj[1]
625 625 except:
626 626 ds = "Alias: " + str(obj)
627 627 else:
628 628 ds = "Alias to " + str(obj)
629 629 if obj.__doc__:
630 630 ds += "\nDocstring:\n" + obj.__doc__
631 631 else:
632 632 ds = getdoc(obj)
633 633 if ds is None:
634 634 ds = '<no docstring>'
635 635 if formatter is not None:
636 636 ds = formatter(ds)
637 637
638 638 # store output in a dict, we initialize it here and fill it as we go
639 639 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
640 640
641 641 string_max = 200 # max size of strings to show (snipped if longer)
642 642 shalf = int((string_max -5)/2)
643 643
644 644 if ismagic:
645 645 obj_type_name = 'Magic function'
646 646 elif isalias:
647 647 obj_type_name = 'System alias'
648 648 else:
649 649 obj_type_name = obj_type.__name__
650 650 out['type_name'] = obj_type_name
651 651
652 652 try:
653 653 bclass = obj.__class__
654 654 out['base_class'] = str(bclass)
655 655 except: pass
656 656
657 657 # String form, but snip if too long in ? form (full in ??)
658 658 if detail_level >= self.str_detail_level:
659 659 try:
660 660 ostr = str(obj)
661 661 str_head = 'string_form'
662 662 if not detail_level and len(ostr)>string_max:
663 663 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
664 664 ostr = ("\n" + " " * len(str_head.expandtabs())).\
665 665 join(q.strip() for q in ostr.split("\n"))
666 666 out[str_head] = ostr
667 667 except:
668 668 pass
669 669
670 670 if ospace:
671 671 out['namespace'] = ospace
672 672
673 673 # Length (for strings and lists)
674 674 try:
675 675 out['length'] = str(len(obj))
676 676 except: pass
677 677
678 678 # Filename where object was defined
679 679 binary_file = False
680 680 fname = find_file(obj)
681 681 if fname is None:
682 682 # if anything goes wrong, we don't want to show source, so it's as
683 683 # if the file was binary
684 684 binary_file = True
685 685 else:
686 686 if fname.endswith(('.so', '.dll', '.pyd')):
687 687 binary_file = True
688 688 elif fname.endswith('<string>'):
689 689 fname = 'Dynamically generated function. No source code available.'
690 690 out['file'] = fname
691 691
692 692 # reconstruct the function definition and print it:
693 693 defln = self._getdef(obj, oname)
694 694 if defln:
695 695 out['definition'] = self.format(defln)
696 696
697 697 # Docstrings only in detail 0 mode, since source contains them (we
698 698 # avoid repetitions). If source fails, we add them back, see below.
699 699 if ds and detail_level == 0:
700 700 out['docstring'] = ds
701 701
702 702 # Original source code for any callable
703 703 if detail_level:
704 704 # Flush the source cache because inspect can return out-of-date
705 705 # source
706 706 linecache.checkcache()
707 707 source = None
708 708 try:
709 709 try:
710 710 source = getsource(obj, binary_file)
711 711 except TypeError:
712 712 if hasattr(obj, '__class__'):
713 713 source = getsource(obj.__class__, binary_file)
714 714 if source is not None:
715 715 out['source'] = source.rstrip()
716 716 except Exception:
717 717 pass
718 718
719 719 if ds and source is None:
720 720 out['docstring'] = ds
721 721
722 722
723 723 # Constructor docstring for classes
724 724 if inspect.isclass(obj):
725 725 out['isclass'] = True
726 726 # reconstruct the function definition and print it:
727 727 try:
728 728 obj_init = obj.__init__
729 729 except AttributeError:
730 730 init_def = init_ds = None
731 731 else:
732 732 init_def = self._getdef(obj_init,oname)
733 733 init_ds = getdoc(obj_init)
734 734 # Skip Python's auto-generated docstrings
735 735 if init_ds and \
736 736 init_ds.startswith('x.__init__(...) initializes'):
737 737 init_ds = None
738 738
739 739 if init_def or init_ds:
740 740 if init_def:
741 741 out['init_definition'] = self.format(init_def)
742 742 if init_ds:
743 743 out['init_docstring'] = init_ds
744 744
745 745 # and class docstring for instances:
746 746 else:
747 747 # First, check whether the instance docstring is identical to the
748 748 # class one, and print it separately if they don't coincide. In
749 749 # most cases they will, but it's nice to print all the info for
750 750 # objects which use instance-customized docstrings.
751 751 if ds:
752 752 try:
753 753 cls = getattr(obj,'__class__')
754 754 except:
755 755 class_ds = None
756 756 else:
757 757 class_ds = getdoc(cls)
758 758 # Skip Python's auto-generated docstrings
759 759 if class_ds and \
760 760 (class_ds.startswith('function(code, globals[,') or \
761 761 class_ds.startswith('instancemethod(function, instance,') or \
762 762 class_ds.startswith('module(name[,') ):
763 763 class_ds = None
764 764 if class_ds and ds != class_ds:
765 765 out['class_docstring'] = class_ds
766 766
767 767 # Next, try to show constructor docstrings
768 768 try:
769 769 init_ds = getdoc(obj.__init__)
770 770 # Skip Python's auto-generated docstrings
771 771 if init_ds and \
772 772 init_ds.startswith('x.__init__(...) initializes'):
773 773 init_ds = None
774 774 except AttributeError:
775 775 init_ds = None
776 776 if init_ds:
777 777 out['init_docstring'] = init_ds
778 778
779 779 # Call form docstring for callable instances
780 780 if hasattr(obj, '__call__'):
781 781 call_def = self._getdef(obj.__call__, oname)
782 782 if call_def is not None:
783 783 out['call_def'] = self.format(call_def)
784 784 call_ds = getdoc(obj.__call__)
785 785 # Skip Python's auto-generated docstrings
786 786 if call_ds and call_ds.startswith('x.__call__(...) <==> x(...)'):
787 787 call_ds = None
788 788 if call_ds:
789 789 out['call_docstring'] = call_ds
790 790
791 791 # Compute the object's argspec as a callable. The key is to decide
792 792 # whether to pull it from the object itself, from its __init__ or
793 793 # from its __call__ method.
794 794
795 795 if inspect.isclass(obj):
796 796 # Old-style classes need not have an __init__
797 797 callable_obj = getattr(obj, "__init__", None)
798 798 elif callable(obj):
799 799 callable_obj = obj
800 800 else:
801 801 callable_obj = None
802 802
803 803 if callable_obj:
804 804 try:
805 805 args, varargs, varkw, defaults = getargspec(callable_obj)
806 806 except (TypeError, AttributeError):
807 807 # For extensions/builtins we can't retrieve the argspec
808 808 pass
809 809 else:
810 810 out['argspec'] = dict(args=args, varargs=varargs,
811 811 varkw=varkw, defaults=defaults)
812 812
813 813 return object_info(**out)
814 814
815 815
816 816 def psearch(self,pattern,ns_table,ns_search=[],
817 817 ignore_case=False,show_all=False):
818 818 """Search namespaces with wildcards for objects.
819 819
820 820 Arguments:
821 821
822 822 - pattern: string containing shell-like wildcards to use in namespace
823 823 searches and optionally a type specification to narrow the search to
824 824 objects of that type.
825 825
826 826 - ns_table: dict of name->namespaces for search.
827 827
828 828 Optional arguments:
829 829
830 830 - ns_search: list of namespace names to include in search.
831 831
832 832 - ignore_case(False): make the search case-insensitive.
833 833
834 834 - show_all(False): show all names, including those starting with
835 835 underscores.
836 836 """
837 837 #print 'ps pattern:<%r>' % pattern # dbg
838 838
839 839 # defaults
840 840 type_pattern = 'all'
841 841 filter = ''
842 842
843 843 cmds = pattern.split()
844 844 len_cmds = len(cmds)
845 845 if len_cmds == 1:
846 846 # Only filter pattern given
847 847 filter = cmds[0]
848 848 elif len_cmds == 2:
849 849 # Both filter and type specified
850 850 filter,type_pattern = cmds
851 851 else:
852 852 raise ValueError('invalid argument string for psearch: <%s>' %
853 853 pattern)
854 854
855 855 # filter search namespaces
856 856 for name in ns_search:
857 857 if name not in ns_table:
858 858 raise ValueError('invalid namespace <%s>. Valid names: %s' %
859 859 (name,ns_table.keys()))
860 860
861 861 #print 'type_pattern:',type_pattern # dbg
862 862 search_result, namespaces_seen = set(), set()
863 863 for ns_name in ns_search:
864 864 ns = ns_table[ns_name]
865 865 # Normally, locals and globals are the same, so we just check one.
866 866 if id(ns) in namespaces_seen:
867 867 continue
868 868 namespaces_seen.add(id(ns))
869 869 tmp_res = list_namespace(ns, type_pattern, filter,
870 870 ignore_case=ignore_case, show_all=show_all)
871 871 search_result.update(tmp_res)
872 872
873 873 page.page('\n'.join(sorted(search_result)))
@@ -1,272 +1,300 b''
1 1 # encoding: utf-8
2 2 """Tests for code execution (%run and related), which is particularly tricky.
3 3
4 4 Because of how %run manages namespaces, and the fact that we are trying here to
5 5 verify subtle object deletion and reference counting issues, the %run tests
6 6 will be kept in this separate file. This makes it easier to aggregate in one
7 7 place the tricks needed to handle it; most other magics are much easier to test
8 8 and we do so in a common test_magic file.
9 9 """
10 10 from __future__ import absolute_import
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Imports
14 14 #-----------------------------------------------------------------------------
15 15
16 16 import os
17 17 import sys
18 18 import tempfile
19 19
20 20 import nose.tools as nt
21 21 from nose import SkipTest
22 22
23 23 from IPython.testing import decorators as dec
24 24 from IPython.testing import tools as tt
25 25 from IPython.utils import py3compat
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Test functions begin
29 29 #-----------------------------------------------------------------------------
30 30
31 31 def doctest_refbug():
32 32 """Very nasty problem with references held by multiple runs of a script.
33 33 See: https://github.com/ipython/ipython/issues/141
34 34
35 35 In [1]: _ip.clear_main_mod_cache()
36 36 # random
37 37
38 38 In [2]: %run refbug
39 39
40 40 In [3]: call_f()
41 41 lowercased: hello
42 42
43 43 In [4]: %run refbug
44 44
45 45 In [5]: call_f()
46 46 lowercased: hello
47 47 lowercased: hello
48 48 """
49 49
50 50
51 51 def doctest_run_builtins():
52 52 r"""Check that %run doesn't damage __builtins__.
53 53
54 54 In [1]: import tempfile
55 55
56 56 In [2]: bid1 = id(__builtins__)
57 57
58 58 In [3]: fname = tempfile.mkstemp('.py')[1]
59 59
60 60 In [3]: f = open(fname,'w')
61 61
62 62 In [4]: dummy= f.write('pass\n')
63 63
64 64 In [5]: f.flush()
65 65
66 66 In [6]: t1 = type(__builtins__)
67 67
68 68 In [7]: %run $fname
69 69
70 70 In [7]: f.close()
71 71
72 72 In [8]: bid2 = id(__builtins__)
73 73
74 74 In [9]: t2 = type(__builtins__)
75 75
76 76 In [10]: t1 == t2
77 77 Out[10]: True
78 78
79 79 In [10]: bid1 == bid2
80 80 Out[10]: True
81 81
82 82 In [12]: try:
83 83 ....: os.unlink(fname)
84 84 ....: except:
85 85 ....: pass
86 86 ....:
87 87 """
88 88
89 89
90 90 def doctest_run_option_parser():
91 91 r"""Test option parser in %run.
92 92
93 93 In [1]: %run print_argv.py
94 94 []
95 95
96 96 In [2]: %run print_argv.py print*.py
97 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 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 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 134 ['print*.py']
107 135
108 136 """
109 137
110 138
111 139 @py3compat.doctest_refactor_print
112 140 def doctest_reset_del():
113 141 """Test that resetting doesn't cause errors in __del__ methods.
114 142
115 143 In [2]: class A(object):
116 144 ...: def __del__(self):
117 145 ...: print str("Hi")
118 146 ...:
119 147
120 148 In [3]: a = A()
121 149
122 150 In [4]: get_ipython().reset()
123 151 Hi
124 152
125 153 In [5]: 1+1
126 154 Out[5]: 2
127 155 """
128 156
129 157 # For some tests, it will be handy to organize them in a class with a common
130 158 # setup that makes a temp file
131 159
132 160 class TestMagicRunPass(tt.TempFileMixin):
133 161
134 162 def setup(self):
135 163 """Make a valid python temp file."""
136 164 self.mktmp('pass\n')
137 165
138 166 def run_tmpfile(self):
139 167 _ip = get_ipython()
140 168 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
141 169 # See below and ticket https://bugs.launchpad.net/bugs/366353
142 170 _ip.magic('run %s' % self.fname)
143 171
144 172 def run_tmpfile_p(self):
145 173 _ip = get_ipython()
146 174 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
147 175 # See below and ticket https://bugs.launchpad.net/bugs/366353
148 176 _ip.magic('run -p %s' % self.fname)
149 177
150 178 def test_builtins_id(self):
151 179 """Check that %run doesn't damage __builtins__ """
152 180 _ip = get_ipython()
153 181 # Test that the id of __builtins__ is not modified by %run
154 182 bid1 = id(_ip.user_ns['__builtins__'])
155 183 self.run_tmpfile()
156 184 bid2 = id(_ip.user_ns['__builtins__'])
157 185 nt.assert_equal(bid1, bid2)
158 186
159 187 def test_builtins_type(self):
160 188 """Check that the type of __builtins__ doesn't change with %run.
161 189
162 190 However, the above could pass if __builtins__ was already modified to
163 191 be a dict (it should be a module) by a previous use of %run. So we
164 192 also check explicitly that it really is a module:
165 193 """
166 194 _ip = get_ipython()
167 195 self.run_tmpfile()
168 196 nt.assert_equal(type(_ip.user_ns['__builtins__']),type(sys))
169 197
170 198 def test_prompts(self):
171 199 """Test that prompts correctly generate after %run"""
172 200 self.run_tmpfile()
173 201 _ip = get_ipython()
174 202 p2 = _ip.prompt_manager.render('in2').strip()
175 203 nt.assert_equal(p2[:3], '...')
176 204
177 205 def test_run_profile( self ):
178 206 """Test that the option -p, which invokes the profiler, do not
179 207 crash by invoking execfile"""
180 208 _ip = get_ipython()
181 209 self.run_tmpfile_p()
182 210
183 211
184 212 class TestMagicRunSimple(tt.TempFileMixin):
185 213
186 214 def test_simpledef(self):
187 215 """Test that simple class definitions work."""
188 216 src = ("class foo: pass\n"
189 217 "def f(): return foo()")
190 218 self.mktmp(src)
191 219 _ip.magic('run %s' % self.fname)
192 220 _ip.run_cell('t = isinstance(f(), foo)')
193 221 nt.assert_true(_ip.user_ns['t'])
194 222
195 223 def test_obj_del(self):
196 224 """Test that object's __del__ methods are called on exit."""
197 225 if sys.platform == 'win32':
198 226 try:
199 227 import win32api
200 228 except ImportError:
201 229 raise SkipTest("Test requires pywin32")
202 230 src = ("class A(object):\n"
203 231 " def __del__(self):\n"
204 232 " print 'object A deleted'\n"
205 233 "a = A()\n")
206 234 self.mktmp(py3compat.doctest_refactor_print(src))
207 235 if dec.module_not_available('sqlite3'):
208 236 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
209 237 else:
210 238 err = None
211 239 tt.ipexec_validate(self.fname, 'object A deleted', err)
212 240
213 241 @dec.skip_known_failure
214 242 def test_aggressive_namespace_cleanup(self):
215 243 """Test that namespace cleanup is not too aggressive GH-238
216 244
217 245 Returning from another run magic deletes the namespace"""
218 246 # see ticket https://github.com/ipython/ipython/issues/238
219 247 class secondtmp(tt.TempFileMixin): pass
220 248 empty = secondtmp()
221 249 empty.mktmp('')
222 250 src = ("ip = get_ipython()\n"
223 251 "for i in range(5):\n"
224 252 " try:\n"
225 253 " ip.magic('run %s')\n"
226 254 " except NameError as e:\n"
227 255 " print i;break\n" % empty.fname)
228 256 self.mktmp(py3compat.doctest_refactor_print(src))
229 257 _ip.magic('run %s' % self.fname)
230 258 _ip.run_cell('ip == get_ipython()')
231 259 nt.assert_equal(_ip.user_ns['i'], 5)
232 260
233 261 @dec.skip_win32
234 262 def test_tclass(self):
235 263 mydir = os.path.dirname(__file__)
236 264 tc = os.path.join(mydir, 'tclass')
237 265 src = ("%%run '%s' C-first\n"
238 266 "%%run '%s' C-second\n"
239 267 "%%run '%s' C-third\n") % (tc, tc, tc)
240 268 self.mktmp(src, '.ipy')
241 269 out = """\
242 270 ARGV 1-: ['C-first']
243 271 ARGV 1-: ['C-second']
244 272 tclass.py: deleting object: C-first
245 273 ARGV 1-: ['C-third']
246 274 tclass.py: deleting object: C-second
247 275 tclass.py: deleting object: C-third
248 276 """
249 277 if dec.module_not_available('sqlite3'):
250 278 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
251 279 else:
252 280 err = None
253 281 tt.ipexec_validate(self.fname, out, err)
254 282
255 283 def test_run_i_after_reset(self):
256 284 """Check that %run -i still works after %reset (gh-693)"""
257 285 src = "yy = zz\n"
258 286 self.mktmp(src)
259 287 _ip.run_cell("zz = 23")
260 288 _ip.magic('run -i %s' % self.fname)
261 289 nt.assert_equal(_ip.user_ns['yy'], 23)
262 290 _ip.magic('reset -f')
263 291 _ip.run_cell("zz = 23")
264 292 _ip.magic('run -i %s' % self.fname)
265 293 nt.assert_equal(_ip.user_ns['yy'], 23)
266 294
267 295 def test_unicode(self):
268 296 """Check that files in odd encodings are accepted."""
269 297 mydir = os.path.dirname(__file__)
270 298 na = os.path.join(mydir, 'nonascii.py')
271 299 _ip.magic('run "%s"' % na)
272 300 nt.assert_equal(_ip.user_ns['u'], u'Ўт№Ф')
@@ -1,527 +1,521 b''
1 1 """IPython extension to reload modules before executing user code.
2 2
3 3 ``autoreload`` reloads modules automatically before entering the execution of
4 4 code typed at the IPython prompt.
5 5
6 6 This makes for example the following workflow possible:
7 7
8 8 .. sourcecode:: ipython
9 9
10 10 In [1]: %load_ext autoreload
11 11
12 12 In [2]: %autoreload 2
13 13
14 14 In [3]: from foo import some_function
15 15
16 16 In [4]: some_function()
17 17 Out[4]: 42
18 18
19 19 In [5]: # open foo.py in an editor and change some_function to return 43
20 20
21 21 In [6]: some_function()
22 22 Out[6]: 43
23 23
24 24 The module was reloaded without reloading it explicitly, and the object
25 25 imported with ``from foo import ...`` was also updated.
26 26
27 27 Usage
28 28 =====
29 29
30 30 The following magic commands are provided:
31 31
32 32 ``%autoreload``
33 33
34 34 Reload all modules (except those excluded by ``%aimport``)
35 35 automatically now.
36 36
37 37 ``%autoreload 0``
38 38
39 39 Disable automatic reloading.
40 40
41 41 ``%autoreload 1``
42 42
43 43 Reload all modules imported with ``%aimport`` every time before
44 44 executing the Python code typed.
45 45
46 46 ``%autoreload 2``
47 47
48 48 Reload all modules (except those excluded by ``%aimport``) every
49 49 time before executing the Python code typed.
50 50
51 51 ``%aimport``
52 52
53 53 List modules which are to be automatically imported or not to be imported.
54 54
55 55 ``%aimport foo``
56 56
57 57 Import module 'foo' and mark it to be autoreloaded for ``%autoreload 1``
58 58
59 59 ``%aimport -foo``
60 60
61 61 Mark module 'foo' to not be autoreloaded.
62 62
63 63 Caveats
64 64 =======
65 65
66 66 Reloading Python modules in a reliable way is in general difficult,
67 67 and unexpected things may occur. ``%autoreload`` tries to work around
68 68 common pitfalls by replacing function code objects and parts of
69 69 classes previously in the module with new versions. This makes the
70 70 following things to work:
71 71
72 72 - Functions and classes imported via 'from xxx import foo' are upgraded
73 73 to new versions when 'xxx' is reloaded.
74 74
75 75 - Methods and properties of classes are upgraded on reload, so that
76 76 calling 'c.foo()' on an object 'c' created before the reload causes
77 77 the new code for 'foo' to be executed.
78 78
79 79 Some of the known remaining caveats are:
80 80
81 81 - Replacing code objects does not always succeed: changing a @property
82 82 in a class to an ordinary method or a method to a member variable
83 83 can cause problems (but in old objects only).
84 84
85 85 - Functions that are removed (eg. via monkey-patching) from a module
86 86 before it is reloaded are not upgraded.
87 87
88 88 - C extension modules cannot be reloaded, and so cannot be autoreloaded.
89 89 """
90 90 from __future__ import print_function
91 91
92 92 skip_doctest = True
93 93
94 94 #-----------------------------------------------------------------------------
95 95 # Copyright (C) 2000 Thomas Heller
96 96 # Copyright (C) 2008 Pauli Virtanen <pav@iki.fi>
97 97 # Copyright (C) 2012 The IPython Development Team
98 98 #
99 99 # Distributed under the terms of the BSD License. The full license is in
100 100 # the file COPYING, distributed as part of this software.
101 101 #-----------------------------------------------------------------------------
102 102 #
103 103 # This IPython module is written by Pauli Virtanen, based on the autoreload
104 104 # code by Thomas Heller.
105 105
106 106 #-----------------------------------------------------------------------------
107 107 # Imports
108 108 #-----------------------------------------------------------------------------
109 109
110 110 import imp
111 111 import os
112 112 import sys
113 113 import traceback
114 114 import types
115 115 import weakref
116 116
117 117 try:
118 118 # Reload is not defined by default in Python3.
119 119 reload
120 120 except NameError:
121 121 from imp import reload
122 122
123 123 from IPython.utils import pyfile
124 124 from IPython.utils.py3compat import PY3
125 125
126 126 #------------------------------------------------------------------------------
127 127 # Autoreload functionality
128 128 #------------------------------------------------------------------------------
129 129
130 130 def _get_compiled_ext():
131 131 """Official way to get the extension of compiled files (.pyc or .pyo)"""
132 132 for ext, mode, typ in imp.get_suffixes():
133 133 if typ == imp.PY_COMPILED:
134 134 return ext
135 135
136 136
137 137 PY_COMPILED_EXT = _get_compiled_ext()
138 138
139 139
140 140 class ModuleReloader(object):
141 141 enabled = False
142 142 """Whether this reloader is enabled"""
143 143
144 144 failed = {}
145 145 """Modules that failed to reload: {module: mtime-on-failed-reload, ...}"""
146 146
147 147 modules = {}
148 148 """Modules specially marked as autoreloadable."""
149 149
150 150 skip_modules = {}
151 151 """Modules specially marked as not autoreloadable."""
152 152
153 153 check_all = True
154 154 """Autoreload all modules, not just those listed in 'modules'"""
155 155
156 156 old_objects = {}
157 157 """(module-name, name) -> weakref, for replacing old code objects"""
158 158
159 159 def mark_module_skipped(self, module_name):
160 160 """Skip reloading the named module in the future"""
161 161 try:
162 162 del self.modules[module_name]
163 163 except KeyError:
164 164 pass
165 165 self.skip_modules[module_name] = True
166 166
167 167 def mark_module_reloadable(self, module_name):
168 168 """Reload the named module in the future (if it is imported)"""
169 169 try:
170 170 del self.skip_modules[module_name]
171 171 except KeyError:
172 172 pass
173 173 self.modules[module_name] = True
174 174
175 175 def aimport_module(self, module_name):
176 176 """Import a module, and mark it reloadable
177 177
178 178 Returns
179 179 -------
180 180 top_module : module
181 181 The imported module if it is top-level, or the top-level
182 182 top_name : module
183 183 Name of top_module
184 184
185 185 """
186 186 self.mark_module_reloadable(module_name)
187 187
188 188 __import__(module_name)
189 189 top_name = module_name.split('.')[0]
190 190 top_module = sys.modules[top_name]
191 191 return top_module, top_name
192 192
193 193 def check(self, check_all=False):
194 194 """Check whether some modules need to be reloaded."""
195 195
196 196 if not self.enabled and not check_all:
197 197 return
198 198
199 199 if check_all or self.check_all:
200 200 modules = sys.modules.keys()
201 201 else:
202 202 modules = self.modules.keys()
203 203
204 204 for modname in modules:
205 205 m = sys.modules.get(modname, None)
206 206
207 207 if modname in self.skip_modules:
208 208 continue
209 209
210 210 if not hasattr(m, '__file__'):
211 211 continue
212 212
213 213 if m.__name__ == '__main__':
214 214 # we cannot reload(__main__)
215 215 continue
216 216
217 217 filename = m.__file__
218 218 path, ext = os.path.splitext(filename)
219 219
220 220 if ext.lower() == '.py':
221 221 ext = PY_COMPILED_EXT
222 222 pyc_filename = pyfile.cache_from_source(filename)
223 223 py_filename = filename
224 224 else:
225 225 pyc_filename = filename
226 226 try:
227 227 py_filename = pyfile.source_from_cache(filename)
228 228 except ValueError:
229 229 continue
230 230
231 231 try:
232 232 pymtime = os.stat(py_filename).st_mtime
233 233 if pymtime <= os.stat(pyc_filename).st_mtime:
234 234 continue
235 235 if self.failed.get(py_filename, None) == pymtime:
236 236 continue
237 237 except OSError:
238 238 continue
239 239
240 240 try:
241 241 superreload(m, reload, self.old_objects)
242 242 if py_filename in self.failed:
243 243 del self.failed[py_filename]
244 244 except:
245 245 print("[autoreload of %s failed: %s]" % (
246 246 modname, traceback.format_exc(1)), file=sys.stderr)
247 247 self.failed[py_filename] = pymtime
248 248
249 249 #------------------------------------------------------------------------------
250 250 # superreload
251 251 #------------------------------------------------------------------------------
252 252
253 253 if PY3:
254 254 func_attrs = ['__code__', '__defaults__', '__doc__',
255 255 '__closure__', '__globals__', '__dict__']
256 256 else:
257 257 func_attrs = ['func_code', 'func_defaults', 'func_doc',
258 258 'func_closure', 'func_globals', 'func_dict']
259 259
260 260
261 261 def update_function(old, new):
262 262 """Upgrade the code object of a function"""
263 263 for name in func_attrs:
264 264 try:
265 265 setattr(old, name, getattr(new, name))
266 266 except (AttributeError, TypeError):
267 267 pass
268 268
269 269
270 270 def update_class(old, new):
271 271 """Replace stuff in the __dict__ of a class, and upgrade
272 272 method code objects"""
273 273 for key in old.__dict__.keys():
274 274 old_obj = getattr(old, key)
275 275
276 276 try:
277 277 new_obj = getattr(new, key)
278 278 except AttributeError:
279 279 # obsolete attribute: remove it
280 280 try:
281 281 delattr(old, key)
282 282 except (AttributeError, TypeError):
283 283 pass
284 284 continue
285 285
286 286 if update_generic(old_obj, new_obj): continue
287 287
288 288 try:
289 289 setattr(old, key, getattr(new, key))
290 290 except (AttributeError, TypeError):
291 291 pass # skip non-writable attributes
292 292
293 293
294 294 def update_property(old, new):
295 295 """Replace get/set/del functions of a property"""
296 296 update_generic(old.fdel, new.fdel)
297 297 update_generic(old.fget, new.fget)
298 298 update_generic(old.fset, new.fset)
299 299
300 300
301 301 def isinstance2(a, b, typ):
302 302 return isinstance(a, typ) and isinstance(b, typ)
303 303
304 304
305 305 UPDATE_RULES = [
306 306 (lambda a, b: isinstance2(a, b, type),
307 307 update_class),
308 308 (lambda a, b: isinstance2(a, b, types.FunctionType),
309 309 update_function),
310 310 (lambda a, b: isinstance2(a, b, property),
311 311 update_property),
312 312 ]
313 313
314 314
315 315 if PY3:
316 316 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType),
317 317 lambda a, b: update_function(a.__func__, b.__func__)),
318 318 ])
319 319 else:
320 320 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.ClassType),
321 321 update_class),
322 322 (lambda a, b: isinstance2(a, b, types.MethodType),
323 323 lambda a, b: update_function(a.im_func, b.im_func)),
324 324 ])
325 325
326 326
327 327 def update_generic(a, b):
328 328 for type_check, update in UPDATE_RULES:
329 329 if type_check(a, b):
330 330 update(a, b)
331 331 return True
332 332 return False
333 333
334 334
335 335 class StrongRef(object):
336 336 def __init__(self, obj):
337 337 self.obj = obj
338 338 def __call__(self):
339 339 return self.obj
340 340
341 341
342 342 def superreload(module, reload=reload, old_objects={}):
343 343 """Enhanced version of the builtin reload function.
344 344
345 345 superreload remembers objects previously in the module, and
346 346
347 347 - upgrades the class dictionary of every old class in the module
348 348 - upgrades the code object of every old function and method
349 349 - clears the module's namespace before reloading
350 350
351 351 """
352 352
353 353 # collect old objects in the module
354 354 for name, obj in module.__dict__.items():
355 355 if not hasattr(obj, '__module__') or obj.__module__ != module.__name__:
356 356 continue
357 357 key = (module.__name__, name)
358 358 try:
359 359 old_objects.setdefault(key, []).append(weakref.ref(obj))
360 360 except TypeError:
361 361 # weakref doesn't work for all types;
362 362 # create strong references for 'important' cases
363 363 if not PY3 and isinstance(obj, types.ClassType):
364 364 old_objects.setdefault(key, []).append(StrongRef(obj))
365 365
366 366 # reload module
367 367 try:
368 368 # clear namespace first from old cruft
369 369 old_dict = module.__dict__.copy()
370 370 old_name = module.__name__
371 371 module.__dict__.clear()
372 372 module.__dict__['__name__'] = old_name
373 373 module.__dict__['__loader__'] = old_dict['__loader__']
374 374 except (TypeError, AttributeError, KeyError):
375 375 pass
376 376
377 377 try:
378 378 module = reload(module)
379 379 except:
380 380 # restore module dictionary on failed reload
381 381 module.__dict__.update(old_dict)
382 382 raise
383 383
384 384 # iterate over all objects and update functions & classes
385 385 for name, new_obj in module.__dict__.items():
386 386 key = (module.__name__, name)
387 387 if key not in old_objects: continue
388 388
389 389 new_refs = []
390 390 for old_ref in old_objects[key]:
391 391 old_obj = old_ref()
392 392 if old_obj is None: continue
393 393 new_refs.append(old_ref)
394 394 update_generic(old_obj, new_obj)
395 395
396 396 if new_refs:
397 397 old_objects[key] = new_refs
398 398 else:
399 399 del old_objects[key]
400 400
401 401 return module
402 402
403 403 #------------------------------------------------------------------------------
404 404 # IPython connectivity
405 405 #------------------------------------------------------------------------------
406 406
407 407 from IPython.core.hooks import TryNext
408 408 from IPython.core.magic import Magics, magics_class, line_magic
409 409
410 410 @magics_class
411 411 class AutoreloadMagics(Magics):
412 412 def __init__(self, *a, **kw):
413 413 super(AutoreloadMagics, self).__init__(*a, **kw)
414 414 self._reloader = ModuleReloader()
415 415 self._reloader.check_all = False
416 416
417 417 @line_magic
418 418 def autoreload(self, parameter_s=''):
419 419 r"""%autoreload => Reload modules automatically
420 420
421 421 %autoreload
422 422 Reload all modules (except those excluded by %aimport) automatically
423 423 now.
424 424
425 425 %autoreload 0
426 426 Disable automatic reloading.
427 427
428 428 %autoreload 1
429 429 Reload all modules imported with %aimport every time before executing
430 430 the Python code typed.
431 431
432 432 %autoreload 2
433 433 Reload all modules (except those excluded by %aimport) every time
434 434 before executing the Python code typed.
435 435
436 436 Reloading Python modules in a reliable way is in general
437 437 difficult, and unexpected things may occur. %autoreload tries to
438 438 work around common pitfalls by replacing function code objects and
439 439 parts of classes previously in the module with new versions. This
440 440 makes the following things to work:
441 441
442 442 - Functions and classes imported via 'from xxx import foo' are upgraded
443 443 to new versions when 'xxx' is reloaded.
444 444
445 445 - Methods and properties of classes are upgraded on reload, so that
446 446 calling 'c.foo()' on an object 'c' created before the reload causes
447 447 the new code for 'foo' to be executed.
448 448
449 449 Some of the known remaining caveats are:
450 450
451 451 - Replacing code objects does not always succeed: changing a @property
452 452 in a class to an ordinary method or a method to a member variable
453 453 can cause problems (but in old objects only).
454 454
455 455 - Functions that are removed (eg. via monkey-patching) from a module
456 456 before it is reloaded are not upgraded.
457 457
458 458 - C extension modules cannot be reloaded, and so cannot be
459 459 autoreloaded.
460 460
461 461 """
462 462 if parameter_s == '':
463 463 self._reloader.check(True)
464 464 elif parameter_s == '0':
465 465 self._reloader.enabled = False
466 466 elif parameter_s == '1':
467 467 self._reloader.check_all = False
468 468 self._reloader.enabled = True
469 469 elif parameter_s == '2':
470 470 self._reloader.check_all = True
471 471 self._reloader.enabled = True
472 472
473 473 @line_magic
474 474 def aimport(self, parameter_s='', stream=None):
475 475 """%aimport => Import modules for automatic reloading.
476 476
477 477 %aimport
478 478 List modules to automatically import and not to import.
479 479
480 480 %aimport foo
481 481 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
482 482
483 483 %aimport -foo
484 484 Mark module 'foo' to not be autoreloaded for %autoreload 1
485 485 """
486 486 modname = parameter_s
487 487 if not modname:
488 488 to_reload = self._reloader.modules.keys()
489 489 to_reload.sort()
490 490 to_skip = self._reloader.skip_modules.keys()
491 491 to_skip.sort()
492 492 if stream is None:
493 493 stream = sys.stdout
494 494 if self._reloader.check_all:
495 495 stream.write("Modules to reload:\nall-except-skipped\n")
496 496 else:
497 497 stream.write("Modules to reload:\n%s\n" % ' '.join(to_reload))
498 498 stream.write("\nModules to skip:\n%s\n" % ' '.join(to_skip))
499 499 elif modname.startswith('-'):
500 500 modname = modname[1:]
501 501 self._reloader.mark_module_skipped(modname)
502 502 else:
503 503 top_module, top_name = self._reloader.aimport_module(modname)
504 504
505 505 # Inject module to user namespace
506 506 self.shell.push({top_name: top_module})
507 507
508 508 def pre_run_code_hook(self, ip):
509 509 if not self._reloader.enabled:
510 510 raise TryNext
511 511 try:
512 512 self._reloader.check()
513 513 except:
514 514 pass
515 515
516 516
517 _loaded = False
518
519
520 517 def load_ipython_extension(ip):
521 518 """Load the extension in IPython."""
522 global _loaded
523 if not _loaded:
524 auto_reload = AutoreloadMagics(ip)
525 ip.register_magics(auto_reload)
526 ip.set_hook('pre_run_code_hook', auto_reload.pre_run_code_hook)
527 _loaded = True
519 auto_reload = AutoreloadMagics(ip)
520 ip.register_magics(auto_reload)
521 ip.set_hook('pre_run_code_hook', auto_reload.pre_run_code_hook)
@@ -1,283 +1,279 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Cython related magics.
4 4
5 5 Author:
6 6 * Brian Granger
7 7
8 8 Parts of this code were taken from Cython.inline.
9 9 """
10 10 #-----------------------------------------------------------------------------
11 11 # Copyright (C) 2010-2011, IPython Development Team.
12 12 #
13 13 # Distributed under the terms of the Modified BSD License.
14 14 #
15 15 # The full license is in the file COPYING.txt, distributed with this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 from __future__ import print_function
19 19
20 20 import imp
21 21 import io
22 22 import os
23 23 import re
24 24 import sys
25 25 import time
26 26
27 27 try:
28 28 import hashlib
29 29 except ImportError:
30 30 import md5 as hashlib
31 31
32 32 from distutils.core import Distribution, Extension
33 33 from distutils.command.build_ext import build_ext
34 34
35 35 from IPython.core import display
36 36 from IPython.core import magic_arguments
37 37 from IPython.core.magic import Magics, magics_class, cell_magic
38 38 from IPython.testing.skipdoctest import skip_doctest
39 39 from IPython.utils import py3compat
40 40
41 41 import Cython
42 42 from Cython.Compiler.Errors import CompileError
43 43 from Cython.Build.Dependencies import cythonize
44 44
45 45
46 46 @magics_class
47 47 class CythonMagics(Magics):
48 48
49 49 def __init__(self, shell):
50 50 super(CythonMagics,self).__init__(shell)
51 51 self._reloads = {}
52 52 self._code_cache = {}
53 53
54 54 def _import_all(self, module):
55 55 for k,v in module.__dict__.items():
56 56 if not k.startswith('__'):
57 57 self.shell.push({k:v})
58 58
59 59 @cell_magic
60 60 def cython_inline(self, line, cell):
61 61 """Compile and run a Cython code cell using Cython.inline.
62 62
63 63 This magic simply passes the body of the cell to Cython.inline
64 64 and returns the result. If the variables `a` and `b` are defined
65 65 in the user's namespace, here is a simple example that returns
66 66 their sum::
67 67
68 68 %%cython_inline
69 69 return a+b
70 70
71 71 For most purposes, we recommend the usage of the `%%cython` magic.
72 72 """
73 73 locs = self.shell.user_global_ns
74 74 globs = self.shell.user_ns
75 75 return Cython.inline(cell, locals=locs, globals=globs)
76 76
77 77 @cell_magic
78 78 def cython_pyximport(self, line, cell):
79 79 """Compile and import a Cython code cell using pyximport.
80 80
81 81 The contents of the cell are written to a `.pyx` file in the current
82 82 working directory, which is then imported using `pyximport`. This
83 83 magic requires a module name to be passed::
84 84
85 85 %%cython_pyximport modulename
86 86 def f(x):
87 87 return 2.0*x
88 88
89 89 The compiled module is then imported and all of its symbols are
90 90 injected into the user's namespace. For most purposes, we recommend
91 91 the usage of the `%%cython` magic.
92 92 """
93 93 module_name = line.strip()
94 94 if not module_name:
95 95 raise ValueError('module name must be given')
96 96 fname = module_name + '.pyx'
97 97 with io.open(fname, 'w', encoding='utf-8') as f:
98 98 f.write(cell)
99 99 if 'pyximport' not in sys.modules:
100 100 import pyximport
101 101 pyximport.install(reload_support=True)
102 102 if module_name in self._reloads:
103 103 module = self._reloads[module_name]
104 104 reload(module)
105 105 else:
106 106 __import__(module_name)
107 107 module = sys.modules[module_name]
108 108 self._reloads[module_name] = module
109 109 self._import_all(module)
110 110
111 111 @magic_arguments.magic_arguments()
112 112 @magic_arguments.argument(
113 113 '-c', '--compile-args', action='append', default=[],
114 114 help="Extra flags to pass to compiler via the `extra_compile_args` "
115 115 "Extension flag (can be specified multiple times)."
116 116 )
117 117 @magic_arguments.argument(
118 118 '-la', '--link-args', action='append', default=[],
119 119 help="Extra flags to pass to linker via the `extra_link_args` "
120 120 "Extension flag (can be specified multiple times)."
121 121 )
122 122 @magic_arguments.argument(
123 123 '-l', '--lib', action='append', default=[],
124 124 help="Add a library to link the extension against (can be specified "
125 125 "multiple times)."
126 126 )
127 127 @magic_arguments.argument(
128 128 '-L', dest='library_dirs', metavar='dir', action='append', default=[],
129 129 help="Add a path to the list of libary directories (can be specified "
130 130 "multiple times)."
131 131 )
132 132 @magic_arguments.argument(
133 133 '-I', '--include', action='append', default=[],
134 134 help="Add a path to the list of include directories (can be specified "
135 135 "multiple times)."
136 136 )
137 137 @magic_arguments.argument(
138 138 '-+', '--cplus', action='store_true', default=False,
139 139 help="Output a C++ rather than C file."
140 140 )
141 141 @magic_arguments.argument(
142 142 '-f', '--force', action='store_true', default=False,
143 143 help="Force the compilation of a new module, even if the source has been "
144 144 "previously compiled."
145 145 )
146 146 @magic_arguments.argument(
147 147 '-a', '--annotate', action='store_true', default=False,
148 148 help="Produce a colorized HTML version of the source."
149 149 )
150 150 @cell_magic
151 151 def cython(self, line, cell):
152 152 """Compile and import everything from a Cython code cell.
153 153
154 154 The contents of the cell are written to a `.pyx` file in the
155 155 directory `IPYTHONDIR/cython` using a filename with the hash of the
156 156 code. This file is then cythonized and compiled. The resulting module
157 157 is imported and all of its symbols are injected into the user's
158 158 namespace. The usage is similar to that of `%%cython_pyximport` but
159 159 you don't have to pass a module name::
160 160
161 161 %%cython
162 162 def f(x):
163 163 return 2.0*x
164 164 """
165 165 args = magic_arguments.parse_argstring(self.cython, line)
166 166 code = cell if cell.endswith('\n') else cell+'\n'
167 167 lib_dir = os.path.join(self.shell.ipython_dir, 'cython')
168 168 quiet = True
169 169 key = code, sys.version_info, sys.executable, Cython.__version__
170 170
171 171 if not os.path.exists(lib_dir):
172 172 os.makedirs(lib_dir)
173 173
174 174 if args.force:
175 175 # Force a new module name by adding the current time to the
176 176 # key which is hashed to determine the module name.
177 177 key += time.time(),
178 178
179 179 module_name = "_cython_magic_" + hashlib.md5(str(key).encode('utf-8')).hexdigest()
180 180 module_path = os.path.join(lib_dir, module_name + self.so_ext)
181 181
182 182 have_module = os.path.isfile(module_path)
183 183 need_cythonize = not have_module
184 184
185 185 if args.annotate:
186 186 html_file = os.path.join(lib_dir, module_name + '.html')
187 187 if not os.path.isfile(html_file):
188 188 need_cythonize = True
189 189
190 190 if need_cythonize:
191 191 c_include_dirs = args.include
192 192 if 'numpy' in code:
193 193 import numpy
194 194 c_include_dirs.append(numpy.get_include())
195 195 pyx_file = os.path.join(lib_dir, module_name + '.pyx')
196 196 pyx_file = py3compat.cast_bytes_py2(pyx_file, encoding=sys.getfilesystemencoding())
197 197 with io.open(pyx_file, 'w', encoding='utf-8') as f:
198 198 f.write(code)
199 199 extension = Extension(
200 200 name = module_name,
201 201 sources = [pyx_file],
202 202 include_dirs = c_include_dirs,
203 203 library_dirs = args.library_dirs,
204 204 extra_compile_args = args.compile_args,
205 205 extra_link_args = args.link_args,
206 206 libraries = args.lib,
207 207 language = 'c++' if args.cplus else 'c',
208 208 )
209 209 build_extension = self._get_build_extension()
210 210 try:
211 211 opts = dict(
212 212 quiet=quiet,
213 213 annotate = args.annotate,
214 214 force = True,
215 215 )
216 216 build_extension.extensions = cythonize([extension], **opts)
217 217 except CompileError:
218 218 return
219 219
220 220 if not have_module:
221 221 build_extension.build_temp = os.path.dirname(pyx_file)
222 222 build_extension.build_lib = lib_dir
223 223 build_extension.run()
224 224 self._code_cache[key] = module_name
225 225
226 226 module = imp.load_dynamic(module_name, module_path)
227 227 self._import_all(module)
228 228
229 229 if args.annotate:
230 230 try:
231 231 with io.open(html_file, encoding='utf-8') as f:
232 232 annotated_html = f.read()
233 233 except IOError as e:
234 234 # File could not be opened. Most likely the user has a version
235 235 # of Cython before 0.15.1 (when `cythonize` learned the
236 236 # `force` keyword argument) and has already compiled this
237 237 # exact source without annotation.
238 238 print('Cython completed successfully but the annotated '
239 239 'source could not be read.', file=sys.stderr)
240 240 print(e, file=sys.stderr)
241 241 else:
242 242 return display.HTML(self.clean_annotated_html(annotated_html))
243 243
244 244 @property
245 245 def so_ext(self):
246 246 """The extension suffix for compiled modules."""
247 247 try:
248 248 return self._so_ext
249 249 except AttributeError:
250 250 self._so_ext = self._get_build_extension().get_ext_filename('')
251 251 return self._so_ext
252 252
253 253 def _get_build_extension(self):
254 254 dist = Distribution()
255 255 config_files = dist.find_config_files()
256 256 try:
257 257 config_files.remove('setup.cfg')
258 258 except ValueError:
259 259 pass
260 260 dist.parse_config_files(config_files)
261 261 build_extension = build_ext(dist)
262 262 build_extension.finalize_options()
263 263 return build_extension
264 264
265 265 @staticmethod
266 266 def clean_annotated_html(html):
267 267 """Clean up the annotated HTML source.
268 268
269 269 Strips the link to the generated C or C++ file, which we do not
270 270 present to the user.
271 271 """
272 272 r = re.compile('<p>Raw output: <a href="(.*)">(.*)</a>')
273 273 html = '\n'.join(l for l in html.splitlines() if not r.match(l))
274 274 return html
275 275
276 _loaded = False
277 276
278 277 def load_ipython_extension(ip):
279 278 """Load the extension in IPython."""
280 global _loaded
281 if not _loaded:
282 ip.register_magics(CythonMagics)
283 _loaded = True
279 ip.register_magics(CythonMagics)
@@ -1,371 +1,367 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 ===========
4 4 octavemagic
5 5 ===========
6 6
7 7 Magics for interacting with Octave via oct2py.
8 8
9 9 .. note::
10 10
11 11 The ``oct2py`` module needs to be installed separately and
12 12 can be obtained using ``easy_install`` or ``pip``.
13 13
14 14 Usage
15 15 =====
16 16
17 17 ``%octave``
18 18
19 19 {OCTAVE_DOC}
20 20
21 21 ``%octave_push``
22 22
23 23 {OCTAVE_PUSH_DOC}
24 24
25 25 ``%octave_pull``
26 26
27 27 {OCTAVE_PULL_DOC}
28 28
29 29 """
30 30
31 31 #-----------------------------------------------------------------------------
32 32 # Copyright (C) 2012 The IPython Development Team
33 33 #
34 34 # Distributed under the terms of the BSD License. The full license is in
35 35 # the file COPYING, distributed as part of this software.
36 36 #-----------------------------------------------------------------------------
37 37
38 38 import tempfile
39 39 from glob import glob
40 40 from shutil import rmtree
41 41
42 42 import numpy as np
43 43 import oct2py
44 44 from xml.dom import minidom
45 45
46 46 from IPython.core.displaypub import publish_display_data
47 47 from IPython.core.magic import (Magics, magics_class, line_magic,
48 48 line_cell_magic, needs_local_scope)
49 49 from IPython.testing.skipdoctest import skip_doctest
50 50 from IPython.core.magic_arguments import (
51 51 argument, magic_arguments, parse_argstring
52 52 )
53 53 from IPython.utils.py3compat import unicode_to_str
54 54
55 55 class OctaveMagicError(oct2py.Oct2PyError):
56 56 pass
57 57
58 58 _mimetypes = {'png' : 'image/png',
59 59 'svg' : 'image/svg+xml',
60 60 'jpg' : 'image/jpeg',
61 61 'jpeg': 'image/jpeg'}
62 62
63 63 @magics_class
64 64 class OctaveMagics(Magics):
65 65 """A set of magics useful for interactive work with Octave via oct2py.
66 66 """
67 67 def __init__(self, shell):
68 68 """
69 69 Parameters
70 70 ----------
71 71 shell : IPython shell
72 72
73 73 """
74 74 super(OctaveMagics, self).__init__(shell)
75 75 self._oct = oct2py.Oct2Py()
76 76 self._plot_format = 'png'
77 77
78 78 # Allow publish_display_data to be overridden for
79 79 # testing purposes.
80 80 self._publish_display_data = publish_display_data
81 81
82 82
83 83 def _fix_gnuplot_svg_size(self, image, size=None):
84 84 """
85 85 GnuPlot SVGs do not have height/width attributes. Set
86 86 these to be the same as the viewBox, so that the browser
87 87 scales the image correctly.
88 88
89 89 Parameters
90 90 ----------
91 91 image : str
92 92 SVG data.
93 93 size : tuple of int
94 94 Image width, height.
95 95
96 96 """
97 97 (svg,) = minidom.parseString(image).getElementsByTagName('svg')
98 98 viewbox = svg.getAttribute('viewBox').split(' ')
99 99
100 100 if size is not None:
101 101 width, height = size
102 102 else:
103 103 width, height = viewbox[2:]
104 104
105 105 svg.setAttribute('width', '%dpx' % width)
106 106 svg.setAttribute('height', '%dpx' % height)
107 107 return svg.toxml()
108 108
109 109
110 110 @skip_doctest
111 111 @line_magic
112 112 def octave_push(self, line):
113 113 '''
114 114 Line-level magic that pushes a variable to Octave.
115 115
116 116 `line` should be made up of whitespace separated variable names in the
117 117 IPython namespace::
118 118
119 119 In [7]: import numpy as np
120 120
121 121 In [8]: X = np.arange(5)
122 122
123 123 In [9]: X.mean()
124 124 Out[9]: 2.0
125 125
126 126 In [10]: %octave_push X
127 127
128 128 In [11]: %octave mean(X)
129 129 Out[11]: 2.0
130 130
131 131 '''
132 132 inputs = line.split(' ')
133 133 for input in inputs:
134 134 input = unicode_to_str(input)
135 135 self._oct.put(input, self.shell.user_ns[input])
136 136
137 137
138 138 @skip_doctest
139 139 @line_magic
140 140 def octave_pull(self, line):
141 141 '''
142 142 Line-level magic that pulls a variable from Octave.
143 143
144 144 In [18]: _ = %octave x = [1 2; 3 4]; y = 'hello'
145 145
146 146 In [19]: %octave_pull x y
147 147
148 148 In [20]: x
149 149 Out[20]:
150 150 array([[ 1., 2.],
151 151 [ 3., 4.]])
152 152
153 153 In [21]: y
154 154 Out[21]: 'hello'
155 155
156 156 '''
157 157 outputs = line.split(' ')
158 158 for output in outputs:
159 159 output = unicode_to_str(output)
160 160 self.shell.push({output: self._oct.get(output)})
161 161
162 162
163 163 @skip_doctest
164 164 @magic_arguments()
165 165 @argument(
166 166 '-i', '--input', action='append',
167 167 help='Names of input variables to be pushed to Octave. Multiple names '
168 168 'can be passed, separated by commas with no whitespace.'
169 169 )
170 170 @argument(
171 171 '-o', '--output', action='append',
172 172 help='Names of variables to be pulled from Octave after executing cell '
173 173 'body. Multiple names can be passed, separated by commas with no '
174 174 'whitespace.'
175 175 )
176 176 @argument(
177 177 '-s', '--size', action='store',
178 178 help='Pixel size of plots, "width,height". Default is "-s 400,250".'
179 179 )
180 180 @argument(
181 181 '-f', '--format', action='store',
182 182 help='Plot format (png, svg or jpg).'
183 183 )
184 184
185 185 @needs_local_scope
186 186 @argument(
187 187 'code',
188 188 nargs='*',
189 189 )
190 190 @line_cell_magic
191 191 def octave(self, line, cell=None, local_ns=None):
192 192 '''
193 193 Execute code in Octave, and pull some of the results back into the
194 194 Python namespace.
195 195
196 196 In [9]: %octave X = [1 2; 3 4]; mean(X)
197 197 Out[9]: array([[ 2., 3.]])
198 198
199 199 As a cell, this will run a block of Octave code, without returning any
200 200 value::
201 201
202 202 In [10]: %%octave
203 203 ....: p = [-2, -1, 0, 1, 2]
204 204 ....: polyout(p, 'x')
205 205
206 206 -2*x^4 - 1*x^3 + 0*x^2 + 1*x^1 + 2
207 207
208 208 In the notebook, plots are published as the output of the cell, e.g.
209 209
210 210 %octave plot([1 2 3], [4 5 6])
211 211
212 212 will create a line plot.
213 213
214 214 Objects can be passed back and forth between Octave and IPython via the
215 215 -i and -o flags in line::
216 216
217 217 In [14]: Z = np.array([1, 4, 5, 10])
218 218
219 219 In [15]: %octave -i Z mean(Z)
220 220 Out[15]: array([ 5.])
221 221
222 222
223 223 In [16]: %octave -o W W = Z * mean(Z)
224 224 Out[16]: array([ 5., 20., 25., 50.])
225 225
226 226 In [17]: W
227 227 Out[17]: array([ 5., 20., 25., 50.])
228 228
229 229 The size and format of output plots can be specified::
230 230
231 231 In [18]: %%octave -s 600,800 -f svg
232 232 ...: plot([1, 2, 3]);
233 233
234 234 '''
235 235 args = parse_argstring(self.octave, line)
236 236
237 237 # arguments 'code' in line are prepended to the cell lines
238 238 if cell is None:
239 239 code = ''
240 240 return_output = True
241 241 else:
242 242 code = cell
243 243 return_output = False
244 244
245 245 code = ' '.join(args.code) + code
246 246
247 247 # if there is no local namespace then default to an empty dict
248 248 if local_ns is None:
249 249 local_ns = {}
250 250
251 251 if args.input:
252 252 for input in ','.join(args.input).split(','):
253 253 input = unicode_to_str(input)
254 254 try:
255 255 val = local_ns[input]
256 256 except KeyError:
257 257 val = self.shell.user_ns[input]
258 258 self._oct.put(input, val)
259 259
260 260 # generate plots in a temporary directory
261 261 plot_dir = tempfile.mkdtemp()
262 262 if args.size is not None:
263 263 size = args.size
264 264 else:
265 265 size = '400,240'
266 266
267 267 if args.format is not None:
268 268 plot_format = args.format
269 269 else:
270 270 plot_format = 'png'
271 271
272 272 pre_call = '''
273 273 global __ipy_figures = [];
274 274 page_screen_output(0);
275 275
276 276 function fig_create(src, event)
277 277 global __ipy_figures;
278 278 __ipy_figures(size(__ipy_figures) + 1) = src;
279 279 set(src, "visible", "off");
280 280 end
281 281
282 282 set(0, 'DefaultFigureCreateFcn', @fig_create);
283 283
284 284 close all;
285 285 clear ans;
286 286
287 287 # ___<end_pre_call>___ #
288 288 '''
289 289
290 290 post_call = '''
291 291 # ___<start_post_call>___ #
292 292
293 293 # Save output of the last execution
294 294 if exist("ans") == 1
295 295 _ = ans;
296 296 else
297 297 _ = nan;
298 298 end
299 299
300 300 for f = __ipy_figures
301 301 outfile = sprintf('%(plot_dir)s/__ipy_oct_fig_%%03d.png', f);
302 302 try
303 303 print(f, outfile, '-d%(plot_format)s', '-tight', '-S%(size)s');
304 304 end
305 305 end
306 306
307 307 ''' % locals()
308 308
309 309 code = ' '.join((pre_call, code, post_call))
310 310 try:
311 311 text_output = self._oct.run(code, verbose=False)
312 312 except (oct2py.Oct2PyError) as exception:
313 313 msg = exception.message
314 314 msg = msg.split('# ___<end_pre_call>___ #')[1]
315 315 msg = msg.split('# ___<start_post_call>___ #')[0]
316 316 raise OctaveMagicError('Octave could not complete execution. '
317 317 'Traceback (currently broken in oct2py): %s'
318 318 % msg)
319 319
320 320 key = 'OctaveMagic.Octave'
321 321 display_data = []
322 322
323 323 # Publish text output
324 324 if text_output:
325 325 display_data.append((key, {'text/plain': text_output}))
326 326
327 327 # Publish images
328 328 images = [open(imgfile, 'rb').read() for imgfile in \
329 329 glob("%s/*" % plot_dir)]
330 330 rmtree(plot_dir)
331 331
332 332 plot_mime_type = _mimetypes.get(plot_format, 'image/png')
333 333 width, height = [int(s) for s in size.split(',')]
334 334 for image in images:
335 335 if plot_format == 'svg':
336 336 image = self._fix_gnuplot_svg_size(image, size=(width, height))
337 337 display_data.append((key, {plot_mime_type: image}))
338 338
339 339 if args.output:
340 340 for output in ','.join(args.output).split(','):
341 341 output = unicode_to_str(output)
342 342 self.shell.push({output: self._oct.get(output)})
343 343
344 344 for source, data in display_data:
345 345 self._publish_display_data(source, data)
346 346
347 347 if return_output:
348 348 ans = self._oct.get('_')
349 349
350 350 # Unfortunately, Octave doesn't have a "None" object,
351 351 # so we can't return any NaN outputs
352 352 if np.isscalar(ans) and np.isnan(ans):
353 353 ans = None
354 354
355 355 return ans
356 356
357 357
358 358 __doc__ = __doc__.format(
359 359 OCTAVE_DOC = ' '*8 + OctaveMagics.octave.__doc__,
360 360 OCTAVE_PUSH_DOC = ' '*8 + OctaveMagics.octave_push.__doc__,
361 361 OCTAVE_PULL_DOC = ' '*8 + OctaveMagics.octave_pull.__doc__
362 362 )
363 363
364 364
365 _loaded = False
366 365 def load_ipython_extension(ip):
367 366 """Load the extension in IPython."""
368 global _loaded
369 if not _loaded:
370 ip.register_magics(OctaveMagics)
371 _loaded = True
367 ip.register_magics(OctaveMagics)
@@ -1,597 +1,593 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 ======
4 4 Rmagic
5 5 ======
6 6
7 7 Magic command interface for interactive work with R via rpy2
8 8
9 9 Usage
10 10 =====
11 11
12 12 ``%R``
13 13
14 14 {R_DOC}
15 15
16 16 ``%Rpush``
17 17
18 18 {RPUSH_DOC}
19 19
20 20 ``%Rpull``
21 21
22 22 {RPULL_DOC}
23 23
24 24 ``%Rget``
25 25
26 26 {RGET_DOC}
27 27
28 28 """
29 29
30 30 #-----------------------------------------------------------------------------
31 31 # Copyright (C) 2012 The IPython Development Team
32 32 #
33 33 # Distributed under the terms of the BSD License. The full license is in
34 34 # the file COPYING, distributed as part of this software.
35 35 #-----------------------------------------------------------------------------
36 36
37 37 import sys
38 38 import tempfile
39 39 from glob import glob
40 40 from shutil import rmtree
41 41 from getopt import getopt
42 42
43 43 # numpy and rpy2 imports
44 44
45 45 import numpy as np
46 46
47 47 import rpy2.rinterface as ri
48 48 import rpy2.robjects as ro
49 49 from rpy2.robjects.numpy2ri import numpy2ri
50 50 ro.conversion.py2ri = numpy2ri
51 51
52 52 # IPython imports
53 53
54 54 from IPython.core.displaypub import publish_display_data
55 55 from IPython.core.magic import (Magics, magics_class, cell_magic, line_magic,
56 56 line_cell_magic, needs_local_scope)
57 57 from IPython.testing.skipdoctest import skip_doctest
58 58 from IPython.core.magic_arguments import (
59 59 argument, magic_arguments, parse_argstring
60 60 )
61 61 from IPython.utils.py3compat import str_to_unicode, unicode_to_str, PY3
62 62
63 63 class RInterpreterError(ri.RRuntimeError):
64 64 """An error when running R code in a %%R magic cell."""
65 65 def __init__(self, line, err, stdout):
66 66 self.line = line
67 67 self.err = err.rstrip()
68 68 self.stdout = stdout.rstrip()
69 69
70 70 def __unicode__(self):
71 71 s = 'Failed to parse and evaluate line %r.\nR error message: %r' % \
72 72 (self.line, self.err)
73 73 if self.stdout and (self.stdout != self.err):
74 74 s += '\nR stdout:\n' + self.stdout
75 75 return s
76 76
77 77 if PY3:
78 78 __str__ = __unicode__
79 79 else:
80 80 def __str__(self):
81 81 return unicode_to_str(unicode(self), 'utf-8')
82 82
83 83 def Rconverter(Robj, dataframe=False):
84 84 """
85 85 Convert an object in R's namespace to one suitable
86 86 for ipython's namespace.
87 87
88 88 For a data.frame, it tries to return a structured array.
89 89 It first checks for colnames, then names.
90 90 If all are NULL, it returns np.asarray(Robj), else
91 91 it tries to construct a recarray
92 92
93 93 Parameters
94 94 ----------
95 95
96 96 Robj: an R object returned from rpy2
97 97 """
98 98 is_data_frame = ro.r('is.data.frame')
99 99 colnames = ro.r('colnames')
100 100 rownames = ro.r('rownames') # with pandas, these could be used for the index
101 101 names = ro.r('names')
102 102
103 103 if dataframe:
104 104 as_data_frame = ro.r('as.data.frame')
105 105 cols = colnames(Robj)
106 106 _names = names(Robj)
107 107 if cols != ri.NULL:
108 108 Robj = as_data_frame(Robj)
109 109 names = tuple(np.array(cols))
110 110 elif _names != ri.NULL:
111 111 names = tuple(np.array(_names))
112 112 else: # failed to find names
113 113 return np.asarray(Robj)
114 114 Robj = np.rec.fromarrays(Robj, names = names)
115 115 return np.asarray(Robj)
116 116
117 117 @magics_class
118 118 class RMagics(Magics):
119 119 """A set of magics useful for interactive work with R via rpy2.
120 120 """
121 121
122 122 def __init__(self, shell, Rconverter=Rconverter,
123 123 pyconverter=np.asarray,
124 124 cache_display_data=False):
125 125 """
126 126 Parameters
127 127 ----------
128 128
129 129 shell : IPython shell
130 130
131 131 pyconverter : callable
132 132 To be called on values in ipython namespace before
133 133 assigning to variables in rpy2.
134 134
135 135 cache_display_data : bool
136 136 If True, the published results of the final call to R are
137 137 cached in the variable 'display_cache'.
138 138
139 139 """
140 140 super(RMagics, self).__init__(shell)
141 141 self.cache_display_data = cache_display_data
142 142
143 143 self.r = ro.R()
144 144
145 145 self.Rstdout_cache = []
146 146 self.pyconverter = pyconverter
147 147 self.Rconverter = Rconverter
148 148
149 149 def eval(self, line):
150 150 '''
151 151 Parse and evaluate a line with rpy2.
152 152 Returns the output to R's stdout() connection
153 153 and the value of eval(parse(line)).
154 154 '''
155 155 old_writeconsole = ri.get_writeconsole()
156 156 ri.set_writeconsole(self.write_console)
157 157 try:
158 158 value = ri.baseenv['eval'](ri.parse(line))
159 159 except (ri.RRuntimeError, ValueError) as exception:
160 160 warning_or_other_msg = self.flush() # otherwise next return seems to have copy of error
161 161 raise RInterpreterError(line, str_to_unicode(str(exception)), warning_or_other_msg)
162 162 text_output = self.flush()
163 163 ri.set_writeconsole(old_writeconsole)
164 164 return text_output, value
165 165
166 166 def write_console(self, output):
167 167 '''
168 168 A hook to capture R's stdout in a cache.
169 169 '''
170 170 self.Rstdout_cache.append(output)
171 171
172 172 def flush(self):
173 173 '''
174 174 Flush R's stdout cache to a string, returning the string.
175 175 '''
176 176 value = ''.join([str_to_unicode(s, 'utf-8') for s in self.Rstdout_cache])
177 177 self.Rstdout_cache = []
178 178 return value
179 179
180 180 @skip_doctest
181 181 @line_magic
182 182 def Rpush(self, line):
183 183 '''
184 184 A line-level magic for R that pushes
185 185 variables from python to rpy2. The line should be made up
186 186 of whitespace separated variable names in the IPython
187 187 namespace::
188 188
189 189 In [7]: import numpy as np
190 190
191 191 In [8]: X = np.array([4.5,6.3,7.9])
192 192
193 193 In [9]: X.mean()
194 194 Out[9]: 6.2333333333333343
195 195
196 196 In [10]: %Rpush X
197 197
198 198 In [11]: %R mean(X)
199 199 Out[11]: array([ 6.23333333])
200 200
201 201 '''
202 202
203 203 inputs = line.split(' ')
204 204 for input in inputs:
205 205 self.r.assign(input, self.pyconverter(self.shell.user_ns[input]))
206 206
207 207 @skip_doctest
208 208 @magic_arguments()
209 209 @argument(
210 210 '-d', '--as_dataframe', action='store_true',
211 211 default=False,
212 212 help='Convert objects to data.frames before returning to ipython.'
213 213 )
214 214 @argument(
215 215 'outputs',
216 216 nargs='*',
217 217 )
218 218 @line_magic
219 219 def Rpull(self, line):
220 220 '''
221 221 A line-level magic for R that pulls
222 222 variables from python to rpy2::
223 223
224 224 In [18]: _ = %R x = c(3,4,6.7); y = c(4,6,7); z = c('a',3,4)
225 225
226 226 In [19]: %Rpull x y z
227 227
228 228 In [20]: x
229 229 Out[20]: array([ 3. , 4. , 6.7])
230 230
231 231 In [21]: y
232 232 Out[21]: array([ 4., 6., 7.])
233 233
234 234 In [22]: z
235 235 Out[22]:
236 236 array(['a', '3', '4'],
237 237 dtype='|S1')
238 238
239 239
240 240 If --as_dataframe, then each object is returned as a structured array
241 241 after first passed through "as.data.frame" in R before
242 242 being calling self.Rconverter.
243 243 This is useful when a structured array is desired as output, or
244 244 when the object in R has mixed data types.
245 245 See the %%R docstring for more examples.
246 246
247 247 Notes
248 248 -----
249 249
250 250 Beware that R names can have '.' so this is not fool proof.
251 251 To avoid this, don't name your R objects with '.'s...
252 252
253 253 '''
254 254 args = parse_argstring(self.Rpull, line)
255 255 outputs = args.outputs
256 256 for output in outputs:
257 257 self.shell.push({output:self.Rconverter(self.r(output),dataframe=args.as_dataframe)})
258 258
259 259 @skip_doctest
260 260 @magic_arguments()
261 261 @argument(
262 262 '-d', '--as_dataframe', action='store_true',
263 263 default=False,
264 264 help='Convert objects to data.frames before returning to ipython.'
265 265 )
266 266 @argument(
267 267 'output',
268 268 nargs=1,
269 269 type=str,
270 270 )
271 271 @line_magic
272 272 def Rget(self, line):
273 273 '''
274 274 Return an object from rpy2, possibly as a structured array (if possible).
275 275 Similar to Rpull except only one argument is accepted and the value is
276 276 returned rather than pushed to self.shell.user_ns::
277 277
278 278 In [3]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
279 279
280 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 282 In [5]: %R -i datapy
283 283
284 284 In [6]: %Rget datapy
285 285 Out[6]:
286 286 array([['1', '2', '3', '4'],
287 287 ['2', '3', '2', '5'],
288 288 ['a', 'b', 'c', 'e']],
289 289 dtype='|S1')
290 290
291 291 In [7]: %Rget -d datapy
292 292 Out[7]:
293 293 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
294 294 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
295 295
296 296 '''
297 297 args = parse_argstring(self.Rget, line)
298 298 output = args.output
299 299 return self.Rconverter(self.r(output[0]),dataframe=args.as_dataframe)
300 300
301 301
302 302 @skip_doctest
303 303 @magic_arguments()
304 304 @argument(
305 305 '-i', '--input', action='append',
306 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 308 @argument(
309 309 '-o', '--output', action='append',
310 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 312 @argument(
313 313 '-w', '--width', type=int,
314 314 help='Width of png plotting device sent as an argument to *png* in R.'
315 315 )
316 316 @argument(
317 317 '-h', '--height', type=int,
318 318 help='Height of png plotting device sent as an argument to *png* in R.'
319 319 )
320 320
321 321 @argument(
322 322 '-d', '--dataframe', action='append',
323 323 help='Convert these objects to data.frames and return as structured arrays.'
324 324 )
325 325 @argument(
326 326 '-u', '--units', type=int,
327 327 help='Units of png plotting device sent as an argument to *png* in R. One of ["px", "in", "cm", "mm"].'
328 328 )
329 329 @argument(
330 330 '-p', '--pointsize', type=int,
331 331 help='Pointsize of png plotting device sent as an argument to *png* in R.'
332 332 )
333 333 @argument(
334 334 '-b', '--bg',
335 335 help='Background of png plotting device sent as an argument to *png* in R.'
336 336 )
337 337 @argument(
338 338 '-n', '--noreturn',
339 339 help='Force the magic to not return anything.',
340 340 action='store_true',
341 341 default=False
342 342 )
343 343 @argument(
344 344 'code',
345 345 nargs='*',
346 346 )
347 347 @needs_local_scope
348 348 @line_cell_magic
349 349 def R(self, line, cell=None, local_ns=None):
350 350 '''
351 351 Execute code in R, and pull some of the results back into the Python namespace.
352 352
353 353 In line mode, this will evaluate an expression and convert the returned value to a Python object.
354 354 The return value is determined by rpy2's behaviour of returning the result of evaluating the
355 355 final line.
356 356
357 357 Multiple R lines can be executed by joining them with semicolons::
358 358
359 359 In [9]: %R X=c(1,4,5,7); sd(X); mean(X)
360 360 Out[9]: array([ 4.25])
361 361
362 362 As a cell, this will run a block of R code, without bringing anything back by default::
363 363
364 364 In [10]: %%R
365 365 ....: Y = c(2,4,3,9)
366 366 ....: print(summary(lm(Y~X)))
367 367 ....:
368 368
369 369 Call:
370 370 lm(formula = Y ~ X)
371 371
372 372 Residuals:
373 373 1 2 3 4
374 374 0.88 -0.24 -2.28 1.64
375 375
376 376 Coefficients:
377 377 Estimate Std. Error t value Pr(>|t|)
378 378 (Intercept) 0.0800 2.3000 0.035 0.975
379 379 X 1.0400 0.4822 2.157 0.164
380 380
381 381 Residual standard error: 2.088 on 2 degrees of freedom
382 382 Multiple R-squared: 0.6993,Adjusted R-squared: 0.549
383 383 F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638
384 384
385 385 In the notebook, plots are published as the output of the cell.
386 386
387 387 %R plot(X, Y)
388 388
389 389 will create a scatter plot of X bs Y.
390 390
391 391 If cell is not None and line has some R code, it is prepended to
392 392 the R code in cell.
393 393
394 394 Objects can be passed back and forth between rpy2 and python via the -i -o flags in line::
395 395
396 396 In [14]: Z = np.array([1,4,5,10])
397 397
398 398 In [15]: %R -i Z mean(Z)
399 399 Out[15]: array([ 5.])
400 400
401 401
402 402 In [16]: %R -o W W=Z*mean(Z)
403 403 Out[16]: array([ 5., 20., 25., 50.])
404 404
405 405 In [17]: W
406 406 Out[17]: array([ 5., 20., 25., 50.])
407 407
408 408 The return value is determined by these rules:
409 409
410 410 * If the cell is not None, the magic returns None.
411 411
412 412 * If the cell evaluates as False, the resulting value is returned
413 413 unless the final line prints something to the console, in
414 414 which case None is returned.
415 415
416 416 * If the final line results in a NULL value when evaluated
417 417 by rpy2, then None is returned.
418 418
419 419 * No attempt is made to convert the final value to a structured array.
420 420 Use the --dataframe flag or %Rget to push / return a structured array.
421 421
422 422 * If the -n flag is present, there is no return value.
423 423
424 424 * A trailing ';' will also result in no return value as the last
425 425 value in the line is an empty string.
426 426
427 427 The --dataframe argument will attempt to return structured arrays.
428 428 This is useful for dataframes with
429 429 mixed data types. Note also that for a data.frame,
430 430 if it is returned as an ndarray, it is transposed::
431 431
432 432 In [18]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
433 433
434 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 436 In [20]: %%R -o datar
437 437 datar = datapy
438 438 ....:
439 439
440 440 In [21]: datar
441 441 Out[21]:
442 442 array([['1', '2', '3', '4'],
443 443 ['2', '3', '2', '5'],
444 444 ['a', 'b', 'c', 'e']],
445 445 dtype='|S1')
446 446
447 447 In [22]: %%R -d datar
448 448 datar = datapy
449 449 ....:
450 450
451 451 In [23]: datar
452 452 Out[23]:
453 453 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
454 454 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
455 455
456 456 The --dataframe argument first tries colnames, then names.
457 457 If both are NULL, it returns an ndarray (i.e. unstructured)::
458 458
459 459 In [1]: %R mydata=c(4,6,8.3); NULL
460 460
461 461 In [2]: %R -d mydata
462 462
463 463 In [3]: mydata
464 464 Out[3]: array([ 4. , 6. , 8.3])
465 465
466 466 In [4]: %R names(mydata) = c('a','b','c'); NULL
467 467
468 468 In [5]: %R -d mydata
469 469
470 470 In [6]: mydata
471 471 Out[6]:
472 472 array((4.0, 6.0, 8.3),
473 473 dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')])
474 474
475 475 In [7]: %R -o mydata
476 476
477 477 In [8]: mydata
478 478 Out[8]: array([ 4. , 6. , 8.3])
479 479
480 480 '''
481 481
482 482 args = parse_argstring(self.R, line)
483 483
484 484 # arguments 'code' in line are prepended to
485 485 # the cell lines
486 486
487 487 if cell is None:
488 488 code = ''
489 489 return_output = True
490 490 line_mode = True
491 491 else:
492 492 code = cell
493 493 return_output = False
494 494 line_mode = False
495 495
496 496 code = ' '.join(args.code) + code
497 497
498 498 # if there is no local namespace then default to an empty dict
499 499 if local_ns is None:
500 500 local_ns = {}
501 501
502 502 if args.input:
503 503 for input in ','.join(args.input).split(','):
504 504 try:
505 505 val = local_ns[input]
506 506 except KeyError:
507 507 val = self.shell.user_ns[input]
508 508 self.r.assign(input, self.pyconverter(val))
509 509
510 510 png_argdict = dict([(n, getattr(args, n)) for n in ['units', 'height', 'width', 'bg', 'pointsize']])
511 511 png_args = ','.join(['%s=%s' % (o,v) for o, v in png_argdict.items() if v is not None])
512 512 # execute the R code in a temporary directory
513 513
514 514 tmpd = tempfile.mkdtemp()
515 515 self.r('png("%s/Rplots%%03d.png",%s)' % (tmpd, png_args))
516 516
517 517 text_output = ''
518 518 if line_mode:
519 519 for line in code.split(';'):
520 520 text_result, result = self.eval(line)
521 521 text_output += text_result
522 522 if text_result:
523 523 # the last line printed something to the console so we won't return it
524 524 return_output = False
525 525 else:
526 526 text_result, result = self.eval(code)
527 527 text_output += text_result
528 528
529 529 self.r('dev.off()')
530 530
531 531 # read out all the saved .png files
532 532
533 533 images = [open(imgfile, 'rb').read() for imgfile in glob("%s/Rplots*png" % tmpd)]
534 534
535 535 # now publish the images
536 536 # mimicking IPython/zmq/pylab/backend_inline.py
537 537 fmt = 'png'
538 538 mimetypes = { 'png' : 'image/png', 'svg' : 'image/svg+xml' }
539 539 mime = mimetypes[fmt]
540 540
541 541 # publish the printed R objects, if any
542 542
543 543 display_data = []
544 544 if text_output:
545 545 display_data.append(('RMagic.R', {'text/plain':text_output}))
546 546
547 547 # flush text streams before sending figures, helps a little with output
548 548 for image in images:
549 549 # synchronization in the console (though it's a bandaid, not a real sln)
550 550 sys.stdout.flush(); sys.stderr.flush()
551 551 display_data.append(('RMagic.R', {mime: image}))
552 552
553 553 # kill the temporary directory
554 554 rmtree(tmpd)
555 555
556 556 # try to turn every output into a numpy array
557 557 # this means that output are assumed to be castable
558 558 # as numpy arrays
559 559
560 560 if args.output:
561 561 for output in ','.join(args.output).split(','):
562 562 self.shell.push({output:self.Rconverter(self.r(output), dataframe=False)})
563 563
564 564 if args.dataframe:
565 565 for output in ','.join(args.dataframe).split(','):
566 566 self.shell.push({output:self.Rconverter(self.r(output), dataframe=True)})
567 567
568 568 for tag, disp_d in display_data:
569 569 publish_display_data(tag, disp_d)
570 570
571 571 # this will keep a reference to the display_data
572 572 # which might be useful to other objects who happen to use
573 573 # this method
574 574
575 575 if self.cache_display_data:
576 576 self.display_cache = display_data
577 577
578 578 # if in line mode and return_output, return the result as an ndarray
579 579 if return_output and not args.noreturn:
580 580 if result != ri.NULL:
581 581 return self.Rconverter(result, dataframe=False)
582 582
583 583 __doc__ = __doc__.format(
584 584 R_DOC = ' '*8 + RMagics.R.__doc__,
585 585 RPUSH_DOC = ' '*8 + RMagics.Rpush.__doc__,
586 586 RPULL_DOC = ' '*8 + RMagics.Rpull.__doc__,
587 587 RGET_DOC = ' '*8 + RMagics.Rget.__doc__
588 588 )
589 589
590 590
591 _loaded = False
592 591 def load_ipython_extension(ip):
593 592 """Load the extension in IPython."""
594 global _loaded
595 if not _loaded:
596 ip.register_magics(RMagics)
597 _loaded = True
593 ip.register_magics(RMagics)
@@ -1,220 +1,214 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 %store magic for lightweight persistence.
4 4
5 5 Stores variables, aliases and macros in IPython's database.
6 6
7 7 To automatically restore stored variables at startup, add this to your
8 8 :file:`ipython_config.py` file::
9 9
10 10 c.StoreMagic.autorestore = True
11 11 """
12 12 #-----------------------------------------------------------------------------
13 13 # Copyright (c) 2012, The IPython Development Team.
14 14 #
15 15 # Distributed under the terms of the Modified BSD License.
16 16 #
17 17 # The full license is in the file COPYING.txt, distributed with this software.
18 18 #-----------------------------------------------------------------------------
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Imports
22 22 #-----------------------------------------------------------------------------
23 23
24 24 # Stdlib
25 25 import inspect, os, sys, textwrap
26 26
27 27 # Our own
28 28 from IPython.core.error import UsageError
29 29 from IPython.core.fakemodule import FakeModule
30 30 from IPython.core.magic import Magics, magics_class, line_magic
31 31 from IPython.testing.skipdoctest import skip_doctest
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Functions and classes
35 35 #-----------------------------------------------------------------------------
36 36
37 37 def restore_aliases(ip):
38 38 staliases = ip.db.get('stored_aliases', {})
39 39 for k,v in staliases.items():
40 40 #print "restore alias",k,v # dbg
41 41 #self.alias_table[k] = v
42 42 ip.alias_manager.define_alias(k,v)
43 43
44 44
45 45 def refresh_variables(ip):
46 46 db = ip.db
47 47 for key in db.keys('autorestore/*'):
48 48 # strip autorestore
49 49 justkey = os.path.basename(key)
50 50 try:
51 51 obj = db[key]
52 52 except KeyError:
53 53 print "Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey
54 54 print "The error was:", sys.exc_info()[0]
55 55 else:
56 56 #print "restored",justkey,"=",obj #dbg
57 57 ip.user_ns[justkey] = obj
58 58
59 59
60 60 def restore_dhist(ip):
61 61 ip.user_ns['_dh'] = ip.db.get('dhist',[])
62 62
63 63
64 64 def restore_data(ip):
65 65 refresh_variables(ip)
66 66 restore_aliases(ip)
67 67 restore_dhist(ip)
68 68
69 69
70 70 @magics_class
71 71 class StoreMagics(Magics):
72 72 """Lightweight persistence for python variables.
73 73
74 74 Provides the %store magic."""
75 75
76 76 @skip_doctest
77 77 @line_magic
78 78 def store(self, parameter_s=''):
79 79 """Lightweight persistence for python variables.
80 80
81 81 Example::
82 82
83 83 In [1]: l = ['hello',10,'world']
84 84 In [2]: %store l
85 85 In [3]: exit
86 86
87 87 (IPython session is closed and started again...)
88 88
89 89 ville@badger:~$ ipython
90 90 In [1]: l
91 91 Out[1]: ['hello', 10, 'world']
92 92
93 93 Usage:
94 94
95 95 * ``%store`` - Show list of all variables and their current
96 96 values
97 97 * ``%store spam`` - Store the *current* value of the variable spam
98 98 to disk
99 99 * ``%store -d spam`` - Remove the variable and its value from storage
100 100 * ``%store -z`` - Remove all variables from storage
101 101 * ``%store -r`` - Refresh all variables from store (delete
102 102 current vals)
103 103 * ``%store foo >a.txt`` - Store value of foo to new file a.txt
104 104 * ``%store foo >>a.txt`` - Append value of foo to file a.txt
105 105
106 106 It should be noted that if you change the value of a variable, you
107 107 need to %store it again if you want to persist the new value.
108 108
109 109 Note also that the variables will need to be pickleable; most basic
110 110 python types can be safely %store'd.
111 111
112 112 Also aliases can be %store'd across sessions.
113 113 """
114 114
115 115 opts,argsl = self.parse_options(parameter_s,'drz',mode='string')
116 116 args = argsl.split(None,1)
117 117 ip = self.shell
118 118 db = ip.db
119 119 # delete
120 120 if 'd' in opts:
121 121 try:
122 122 todel = args[0]
123 123 except IndexError:
124 124 raise UsageError('You must provide the variable to forget')
125 125 else:
126 126 try:
127 127 del db['autorestore/' + todel]
128 128 except:
129 129 raise UsageError("Can't delete variable '%s'" % todel)
130 130 # reset
131 131 elif 'z' in opts:
132 132 for k in db.keys('autorestore/*'):
133 133 del db[k]
134 134
135 135 elif 'r' in opts:
136 136 refresh_variables(ip)
137 137
138 138
139 139 # run without arguments -> list variables & values
140 140 elif not args:
141 141 vars = db.keys('autorestore/*')
142 142 vars.sort()
143 143 if vars:
144 144 size = max(map(len, vars))
145 145 else:
146 146 size = 0
147 147
148 148 print 'Stored variables and their in-db values:'
149 149 fmt = '%-'+str(size)+'s -> %s'
150 150 get = db.get
151 151 for var in vars:
152 152 justkey = os.path.basename(var)
153 153 # print 30 first characters from every var
154 154 print fmt % (justkey, repr(get(var, '<unavailable>'))[:50])
155 155
156 156 # default action - store the variable
157 157 else:
158 158 # %store foo >file.txt or >>file.txt
159 159 if len(args) > 1 and args[1].startswith('>'):
160 160 fnam = os.path.expanduser(args[1].lstrip('>').lstrip())
161 161 if args[1].startswith('>>'):
162 162 fil = open(fnam, 'a')
163 163 else:
164 164 fil = open(fnam, 'w')
165 165 obj = ip.ev(args[0])
166 166 print "Writing '%s' (%s) to file '%s'." % (args[0],
167 167 obj.__class__.__name__, fnam)
168 168
169 169
170 170 if not isinstance (obj, basestring):
171 171 from pprint import pprint
172 172 pprint(obj, fil)
173 173 else:
174 174 fil.write(obj)
175 175 if not obj.endswith('\n'):
176 176 fil.write('\n')
177 177
178 178 fil.close()
179 179 return
180 180
181 181 # %store foo
182 182 try:
183 183 obj = ip.user_ns[args[0]]
184 184 except KeyError:
185 185 # it might be an alias
186 186 # This needs to be refactored to use the new AliasManager stuff.
187 187 if args[0] in ip.alias_manager:
188 188 name = args[0]
189 189 nargs, cmd = ip.alias_manager.alias_table[ name ]
190 190 staliases = db.get('stored_aliases',{})
191 191 staliases[ name ] = cmd
192 192 db['stored_aliases'] = staliases
193 193 print "Alias stored: %s (%s)" % (name, cmd)
194 194 return
195 195 else:
196 196 raise UsageError("Unknown variable '%s'" % args[0])
197 197
198 198 else:
199 199 if isinstance(inspect.getmodule(obj), FakeModule):
200 200 print textwrap.dedent("""\
201 201 Warning:%s is %s
202 202 Proper storage of interactively declared classes (or instances
203 203 of those classes) is not possible! Only instances
204 204 of classes in real modules on file system can be %%store'd.
205 205 """ % (args[0], obj) )
206 206 return
207 207 #pickled = pickle.dumps(obj)
208 208 db[ 'autorestore/' + args[0] ] = obj
209 209 print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__)
210 210
211 211
212 _loaded = False
213
214
215 212 def load_ipython_extension(ip):
216 213 """Load the extension in IPython."""
217 global _loaded
218 if not _loaded:
219 ip.register_magics(StoreMagics)
220 _loaded = True
214 ip.register_magics(StoreMagics)
@@ -1,354 +1,356 b''
1 1 """Basic ssh tunnel utilities, and convenience functions for tunneling
2 2 zeromq connections.
3 3
4 4 Authors
5 5 -------
6 6 * Min RK
7 7 """
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (C) 2010-2011 The IPython Development Team
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 22 from __future__ import print_function
23 23
24 24 import os,sys, atexit
25 import signal
25 26 import socket
26 27 from multiprocessing import Process
27 28 from getpass import getpass, getuser
28 29 import warnings
29 30
30 31 try:
31 32 with warnings.catch_warnings():
32 33 warnings.simplefilter('ignore', DeprecationWarning)
33 34 import paramiko
34 35 except ImportError:
35 36 paramiko = None
36 37 else:
37 38 from forward import forward_tunnel
38 39
39 40 try:
40 41 from IPython.external import pexpect
41 42 except ImportError:
42 43 pexpect = None
43 44
44 45 #-----------------------------------------------------------------------------
45 46 # Code
46 47 #-----------------------------------------------------------------------------
47 48
48 49 # select_random_ports copied from IPython.parallel.util
49 50 _random_ports = set()
50 51
51 52 def select_random_ports(n):
52 53 """Selects and return n random ports that are available."""
53 54 ports = []
54 55 for i in xrange(n):
55 56 sock = socket.socket()
56 57 sock.bind(('', 0))
57 58 while sock.getsockname()[1] in _random_ports:
58 59 sock.close()
59 60 sock = socket.socket()
60 61 sock.bind(('', 0))
61 62 ports.append(sock)
62 63 for i, sock in enumerate(ports):
63 64 port = sock.getsockname()[1]
64 65 sock.close()
65 66 ports[i] = port
66 67 _random_ports.add(port)
67 68 return ports
68 69
69 70
70 71 #-----------------------------------------------------------------------------
71 72 # Check for passwordless login
72 73 #-----------------------------------------------------------------------------
73 74
74 75 def try_passwordless_ssh(server, keyfile, paramiko=None):
75 76 """Attempt to make an ssh connection without a password.
76 77 This is mainly used for requiring password input only once
77 78 when many tunnels may be connected to the same server.
78 79
79 80 If paramiko is None, the default for the platform is chosen.
80 81 """
81 82 if paramiko is None:
82 83 paramiko = sys.platform == 'win32'
83 84 if not paramiko:
84 85 f = _try_passwordless_openssh
85 86 else:
86 87 f = _try_passwordless_paramiko
87 88 return f(server, keyfile)
88 89
89 90 def _try_passwordless_openssh(server, keyfile):
90 91 """Try passwordless login with shell ssh command."""
91 92 if pexpect is None:
92 93 raise ImportError("pexpect unavailable, use paramiko")
93 94 cmd = 'ssh -f '+ server
94 95 if keyfile:
95 96 cmd += ' -i ' + keyfile
96 97 cmd += ' exit'
97 98 p = pexpect.spawn(cmd)
98 99 while True:
99 100 try:
100 101 p.expect('[Pp]assword:', timeout=.1)
101 102 except pexpect.TIMEOUT:
102 103 continue
103 104 except pexpect.EOF:
104 105 return True
105 106 else:
106 107 return False
107 108
108 109 def _try_passwordless_paramiko(server, keyfile):
109 110 """Try passwordless login with paramiko."""
110 111 if paramiko is None:
111 112 msg = "Paramiko unavaliable, "
112 113 if sys.platform == 'win32':
113 114 msg += "Paramiko is required for ssh tunneled connections on Windows."
114 115 else:
115 116 msg += "use OpenSSH."
116 117 raise ImportError(msg)
117 118 username, server, port = _split_server(server)
118 119 client = paramiko.SSHClient()
119 120 client.load_system_host_keys()
120 121 client.set_missing_host_key_policy(paramiko.WarningPolicy())
121 122 try:
122 123 client.connect(server, port, username=username, key_filename=keyfile,
123 124 look_for_keys=True)
124 125 except paramiko.AuthenticationException:
125 126 return False
126 127 else:
127 128 client.close()
128 129 return True
129 130
130 131
131 132 def tunnel_connection(socket, addr, server, keyfile=None, password=None, paramiko=None, timeout=60):
132 133 """Connect a socket to an address via an ssh tunnel.
133 134
134 135 This is a wrapper for socket.connect(addr), when addr is not accessible
135 136 from the local machine. It simply creates an ssh tunnel using the remaining args,
136 137 and calls socket.connect('tcp://localhost:lport') where lport is the randomly
137 138 selected local port of the tunnel.
138 139
139 140 """
140 141 new_url, tunnel = open_tunnel(addr, server, keyfile=keyfile, password=password, paramiko=paramiko, timeout=timeout)
141 142 socket.connect(new_url)
142 143 return tunnel
143 144
144 145
145 146 def open_tunnel(addr, server, keyfile=None, password=None, paramiko=None, timeout=60):
146 147 """Open a tunneled connection from a 0MQ url.
147 148
148 149 For use inside tunnel_connection.
149 150
150 151 Returns
151 152 -------
152 153
153 154 (url, tunnel): The 0MQ url that has been forwarded, and the tunnel object
154 155 """
155 156
156 157 lport = select_random_ports(1)[0]
157 158 transport, addr = addr.split('://')
158 159 ip,rport = addr.split(':')
159 160 rport = int(rport)
160 161 if paramiko is None:
161 162 paramiko = sys.platform == 'win32'
162 163 if paramiko:
163 164 tunnelf = paramiko_tunnel
164 165 else:
165 166 tunnelf = openssh_tunnel
166 167
167 168 tunnel = tunnelf(lport, rport, server, remoteip=ip, keyfile=keyfile, password=password, timeout=timeout)
168 169 return 'tcp://127.0.0.1:%i'%lport, tunnel
169 170
170 171 def openssh_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, password=None, timeout=60):
171 172 """Create an ssh tunnel using command-line ssh that connects port lport
172 173 on this machine to localhost:rport on server. The tunnel
173 174 will automatically close when not in use, remaining open
174 175 for a minimum of timeout seconds for an initial connection.
175 176
176 177 This creates a tunnel redirecting `localhost:lport` to `remoteip:rport`,
177 178 as seen from `server`.
178 179
179 180 keyfile and password may be specified, but ssh config is checked for defaults.
180 181
181 182 Parameters
182 183 ----------
183 184
184 185 lport : int
185 186 local port for connecting to the tunnel from this machine.
186 187 rport : int
187 188 port on the remote machine to connect to.
188 189 server : str
189 190 The ssh server to connect to. The full ssh server string will be parsed.
190 191 user@server:port
191 192 remoteip : str [Default: 127.0.0.1]
192 193 The remote ip, specifying the destination of the tunnel.
193 194 Default is localhost, which means that the tunnel would redirect
194 195 localhost:lport on this machine to localhost:rport on the *server*.
195 196
196 197 keyfile : str; path to public key file
197 198 This specifies a key to be used in ssh login, default None.
198 199 Regular default ssh keys will be used without specifying this argument.
199 200 password : str;
200 201 Your ssh password to the ssh server. Note that if this is left None,
201 202 you will be prompted for it if passwordless key based login is unavailable.
202 203 timeout : int [default: 60]
203 204 The time (in seconds) after which no activity will result in the tunnel
204 205 closing. This prevents orphaned tunnels from running forever.
205 206 """
206 207 if pexpect is None:
207 208 raise ImportError("pexpect unavailable, use paramiko_tunnel")
208 209 ssh="ssh "
209 210 if keyfile:
210 211 ssh += "-i " + keyfile
211 212
212 213 if ':' in server:
213 214 server, port = server.split(':')
214 215 ssh += " -p %s" % port
215 216
216 217 cmd = "%s -f -L 127.0.0.1:%i:%s:%i %s sleep %i" % (
217 218 ssh, lport, remoteip, rport, server, timeout)
218 219 tunnel = pexpect.spawn(cmd)
219 220 failed = False
220 221 while True:
221 222 try:
222 223 tunnel.expect('[Pp]assword:', timeout=.1)
223 224 except pexpect.TIMEOUT:
224 225 continue
225 226 except pexpect.EOF:
226 227 if tunnel.exitstatus:
227 228 print (tunnel.exitstatus)
228 229 print (tunnel.before)
229 230 print (tunnel.after)
230 231 raise RuntimeError("tunnel '%s' failed to start"%(cmd))
231 232 else:
232 233 return tunnel.pid
233 234 else:
234 235 if failed:
235 236 print("Password rejected, try again")
236 237 password=None
237 238 if password is None:
238 239 password = getpass("%s's password: "%(server))
239 240 tunnel.sendline(password)
240 241 failed = True
241 242
242 243 def _split_server(server):
243 244 if '@' in server:
244 245 username,server = server.split('@', 1)
245 246 else:
246 247 username = getuser()
247 248 if ':' in server:
248 249 server, port = server.split(':')
249 250 port = int(port)
250 251 else:
251 252 port = 22
252 253 return username, server, port
253 254
254 255 def paramiko_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, password=None, timeout=60):
255 256 """launch a tunner with paramiko in a subprocess. This should only be used
256 257 when shell ssh is unavailable (e.g. Windows).
257 258
258 259 This creates a tunnel redirecting `localhost:lport` to `remoteip:rport`,
259 260 as seen from `server`.
260 261
261 262 If you are familiar with ssh tunnels, this creates the tunnel:
262 263
263 264 ssh server -L localhost:lport:remoteip:rport
264 265
265 266 keyfile and password may be specified, but ssh config is checked for defaults.
266 267
267 268
268 269 Parameters
269 270 ----------
270 271
271 272 lport : int
272 273 local port for connecting to the tunnel from this machine.
273 274 rport : int
274 275 port on the remote machine to connect to.
275 276 server : str
276 277 The ssh server to connect to. The full ssh server string will be parsed.
277 278 user@server:port
278 279 remoteip : str [Default: 127.0.0.1]
279 280 The remote ip, specifying the destination of the tunnel.
280 281 Default is localhost, which means that the tunnel would redirect
281 282 localhost:lport on this machine to localhost:rport on the *server*.
282 283
283 284 keyfile : str; path to public key file
284 285 This specifies a key to be used in ssh login, default None.
285 286 Regular default ssh keys will be used without specifying this argument.
286 287 password : str;
287 288 Your ssh password to the ssh server. Note that if this is left None,
288 289 you will be prompted for it if passwordless key based login is unavailable.
289 290 timeout : int [default: 60]
290 291 The time (in seconds) after which no activity will result in the tunnel
291 292 closing. This prevents orphaned tunnels from running forever.
292 293
293 294 """
294 295 if paramiko is None:
295 296 raise ImportError("Paramiko not available")
296 297
297 298 if password is None:
298 299 if not _try_passwordless_paramiko(server, keyfile):
299 300 password = getpass("%s's password: "%(server))
300 301
301 302 p = Process(target=_paramiko_tunnel,
302 303 args=(lport, rport, server, remoteip),
303 304 kwargs=dict(keyfile=keyfile, password=password))
304 305 p.daemon=False
305 306 p.start()
306 307 atexit.register(_shutdown_process, p)
307 308 return p
308 309
309 310 def _shutdown_process(p):
310 311 if p.is_alive():
311 312 p.terminate()
312 313
313 314 def _paramiko_tunnel(lport, rport, server, remoteip, keyfile=None, password=None):
314 315 """Function for actually starting a paramiko tunnel, to be passed
315 316 to multiprocessing.Process(target=this), and not called directly.
316 317 """
317 318 username, server, port = _split_server(server)
318 319 client = paramiko.SSHClient()
319 320 client.load_system_host_keys()
320 321 client.set_missing_host_key_policy(paramiko.WarningPolicy())
321 322
322 323 try:
323 324 client.connect(server, port, username=username, key_filename=keyfile,
324 325 look_for_keys=True, password=password)
325 326 # except paramiko.AuthenticationException:
326 327 # if password is None:
327 328 # password = getpass("%s@%s's password: "%(username, server))
328 329 # client.connect(server, port, username=username, password=password)
329 330 # else:
330 331 # raise
331 332 except Exception as e:
332 333 print ('*** Failed to connect to %s:%d: %r' % (server, port, e))
333 334 sys.exit(1)
334
335 # print ('Now forwarding port %d to %s:%d ...' % (lport, server, rport))
336
335
336 # Don't let SIGINT kill the tunnel subprocess
337 signal.signal(signal.SIGINT, signal.SIG_IGN)
338
337 339 try:
338 340 forward_tunnel(lport, remoteip, rport, client.get_transport())
339 341 except KeyboardInterrupt:
340 342 print ('SIGINT: Port forwarding stopped cleanly')
341 343 sys.exit(0)
342 344 except Exception as e:
343 345 print ("Port forwarding stopped uncleanly: %s"%e)
344 346 sys.exit(255)
345 347
346 348 if sys.platform == 'win32':
347 349 ssh_tunnel = paramiko_tunnel
348 350 else:
349 351 ssh_tunnel = openssh_tunnel
350 352
351 353
352 354 __all__ = ['tunnel_connection', 'ssh_tunnel', 'openssh_tunnel', 'paramiko_tunnel', 'try_passwordless_ssh']
353 355
354 356
@@ -1,619 +1,622 b''
1 1 # coding: utf-8
2 2 """A tornado based IPython notebook server.
3 3
4 4 Authors:
5 5
6 6 * Brian Granger
7 7 """
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2008-2011 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 # stdlib
20 20 import errno
21 21 import logging
22 22 import os
23 23 import random
24 24 import re
25 25 import select
26 26 import signal
27 27 import socket
28 28 import sys
29 29 import threading
30 30 import time
31 31 import uuid
32 32 import webbrowser
33 33
34 34 # Third party
35 35 import zmq
36 36
37 37 # Install the pyzmq ioloop. This has to be done before anything else from
38 38 # tornado is imported.
39 39 from zmq.eventloop import ioloop
40 40 ioloop.install()
41 41
42 42 from tornado import httpserver
43 43 from tornado import web
44 44
45 45 # Our own libraries
46 46 from .kernelmanager import MappingKernelManager
47 47 from .handlers import (LoginHandler, LogoutHandler,
48 48 ProjectDashboardHandler, NewHandler, NamedNotebookHandler,
49 49 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
50 50 ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler,
51 51 RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler,
52 52 MainClusterHandler, ClusterProfileHandler, ClusterActionHandler,
53 53 FileFindHandler,
54 54 )
55 55 from .nbmanager import NotebookManager
56 56 from .filenbmanager import FileNotebookManager
57 57 from .clustermanager import ClusterManager
58 58
59 59 from IPython.config.application import catch_config_error, boolean_flag
60 60 from IPython.core.application import BaseIPythonApplication
61 61 from IPython.core.profiledir import ProfileDir
62 62 from IPython.frontend.consoleapp import IPythonConsoleApp
63 63 from IPython.lib.kernel import swallow_argv
64 64 from IPython.zmq.session import Session, default_secure
65 65 from IPython.zmq.zmqshell import ZMQInteractiveShell
66 66 from IPython.zmq.ipkernel import (
67 67 flags as ipkernel_flags,
68 68 aliases as ipkernel_aliases,
69 69 IPKernelApp
70 70 )
71 71 from IPython.utils.importstring import import_item
72 72 from IPython.utils.traitlets import (
73 73 Dict, Unicode, Integer, List, Enum, Bool,
74 74 DottedObjectName
75 75 )
76 76 from IPython.utils import py3compat
77 77 from IPython.utils.path import filefind
78 78
79 79 #-----------------------------------------------------------------------------
80 80 # Module globals
81 81 #-----------------------------------------------------------------------------
82 82
83 83 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
84 84 _kernel_action_regex = r"(?P<action>restart|interrupt)"
85 85 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
86 86 _profile_regex = r"(?P<profile>[^\/]+)" # there is almost no text that is invalid
87 87 _cluster_action_regex = r"(?P<action>start|stop)"
88 88
89 89
90 90 LOCALHOST = '127.0.0.1'
91 91
92 92 _examples = """
93 93 ipython notebook # start the notebook
94 94 ipython notebook --profile=sympy # use the sympy profile
95 95 ipython notebook --pylab=inline # pylab in inline plotting mode
96 96 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
97 97 ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces
98 98 """
99 99
100 100 #-----------------------------------------------------------------------------
101 101 # Helper functions
102 102 #-----------------------------------------------------------------------------
103 103
104 104 def url_path_join(a,b):
105 105 if a.endswith('/') and b.startswith('/'):
106 106 return a[:-1]+b
107 107 else:
108 108 return a+b
109 109
110 110 def random_ports(port, n):
111 111 """Generate a list of n random ports near the given port.
112 112
113 113 The first 5 ports will be sequential, and the remaining n-5 will be
114 114 randomly selected in the range [port-2*n, port+2*n].
115 115 """
116 116 for i in range(min(5, n)):
117 117 yield port + i
118 118 for i in range(n-5):
119 119 yield port + random.randint(-2*n, 2*n)
120 120
121 121 #-----------------------------------------------------------------------------
122 122 # The Tornado web application
123 123 #-----------------------------------------------------------------------------
124 124
125 125 class NotebookWebApplication(web.Application):
126 126
127 127 def __init__(self, ipython_app, kernel_manager, notebook_manager,
128 128 cluster_manager, log,
129 129 base_project_url, settings_overrides):
130 130 handlers = [
131 131 (r"/", ProjectDashboardHandler),
132 132 (r"/login", LoginHandler),
133 133 (r"/logout", LogoutHandler),
134 134 (r"/new", NewHandler),
135 135 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
136 136 (r"/%s/copy" % _notebook_id_regex, NotebookCopyHandler),
137 137 (r"/%s/print" % _notebook_id_regex, PrintNotebookHandler),
138 138 (r"/kernels", MainKernelHandler),
139 139 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
140 140 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
141 141 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
142 142 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
143 143 (r"/notebooks", NotebookRootHandler),
144 144 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
145 145 (r"/rstservice/render", RSTHandler),
146 146 (r"/files/(.*)", AuthenticatedFileHandler, {'path' : notebook_manager.notebook_dir}),
147 147 (r"/clusters", MainClusterHandler),
148 148 (r"/clusters/%s/%s" % (_profile_regex, _cluster_action_regex), ClusterActionHandler),
149 149 (r"/clusters/%s" % _profile_regex, ClusterProfileHandler),
150 150 ]
151 151
152 152 # Python < 2.6.5 doesn't accept unicode keys in f(**kwargs), and
153 153 # base_project_url will always be unicode, which will in turn
154 154 # make the patterns unicode, and ultimately result in unicode
155 155 # keys in kwargs to handler._execute(**kwargs) in tornado.
156 156 # This enforces that base_project_url be ascii in that situation.
157 157 #
158 158 # Note that the URLs these patterns check against are escaped,
159 159 # and thus guaranteed to be ASCII: 'héllo' is really 'h%C3%A9llo'.
160 160 base_project_url = py3compat.unicode_to_str(base_project_url, 'ascii')
161 161
162 162 settings = dict(
163 163 template_path=os.path.join(os.path.dirname(__file__), "templates"),
164 164 static_path=ipython_app.static_file_path,
165 165 static_handler_class = FileFindHandler,
166 166 cookie_secret=os.urandom(1024),
167 167 login_url="%s/login"%(base_project_url.rstrip('/')),
168 168 cookie_name='username-%s' % uuid.uuid4(),
169 169 )
170 170
171 171 # allow custom overrides for the tornado web app.
172 172 settings.update(settings_overrides)
173 173
174 174 # prepend base_project_url onto the patterns that we match
175 175 new_handlers = []
176 176 for handler in handlers:
177 177 pattern = url_path_join(base_project_url, handler[0])
178 178 new_handler = tuple([pattern]+list(handler[1:]))
179 179 new_handlers.append( new_handler )
180 180
181 181 super(NotebookWebApplication, self).__init__(new_handlers, **settings)
182 182
183 183 self.kernel_manager = kernel_manager
184 184 self.notebook_manager = notebook_manager
185 185 self.cluster_manager = cluster_manager
186 186 self.ipython_app = ipython_app
187 187 self.read_only = self.ipython_app.read_only
188 188 self.log = log
189 189
190 190
191 191 #-----------------------------------------------------------------------------
192 192 # Aliases and Flags
193 193 #-----------------------------------------------------------------------------
194 194
195 195 flags = dict(ipkernel_flags)
196 196 flags['no-browser']=(
197 197 {'NotebookApp' : {'open_browser' : False}},
198 198 "Don't open the notebook in a browser after startup."
199 199 )
200 200 flags['no-mathjax']=(
201 201 {'NotebookApp' : {'enable_mathjax' : False}},
202 202 """Disable MathJax
203 203
204 204 MathJax is the javascript library IPython uses to render math/LaTeX. It is
205 205 very large, so you may want to disable it if you have a slow internet
206 206 connection, or for offline use of the notebook.
207 207
208 208 When disabled, equations etc. will appear as their untransformed TeX source.
209 209 """
210 210 )
211 211 flags['read-only'] = (
212 212 {'NotebookApp' : {'read_only' : True}},
213 213 """Allow read-only access to notebooks.
214 214
215 215 When using a password to protect the notebook server, this flag
216 216 allows unauthenticated clients to view the notebook list, and
217 217 individual notebooks, but not edit them, start kernels, or run
218 218 code.
219 219
220 220 If no password is set, the server will be entirely read-only.
221 221 """
222 222 )
223 223
224 224 # Add notebook manager flags
225 225 flags.update(boolean_flag('script', 'FileNotebookManager.save_script',
226 226 'Auto-save a .py script everytime the .ipynb notebook is saved',
227 227 'Do not auto-save .py scripts for every notebook'))
228 228
229 229 # the flags that are specific to the frontend
230 230 # these must be scrubbed before being passed to the kernel,
231 231 # or it will raise an error on unrecognized flags
232 232 notebook_flags = ['no-browser', 'no-mathjax', 'read-only', 'script', 'no-script']
233 233
234 234 aliases = dict(ipkernel_aliases)
235 235
236 236 aliases.update({
237 237 'ip': 'NotebookApp.ip',
238 238 'port': 'NotebookApp.port',
239 239 'port-retries': 'NotebookApp.port_retries',
240 240 'keyfile': 'NotebookApp.keyfile',
241 241 'certfile': 'NotebookApp.certfile',
242 242 'notebook-dir': 'NotebookManager.notebook_dir',
243 243 'browser': 'NotebookApp.browser',
244 244 })
245 245
246 246 # remove ipkernel flags that are singletons, and don't make sense in
247 247 # multi-kernel evironment:
248 248 aliases.pop('f', None)
249 249
250 250 notebook_aliases = [u'port', u'port-retries', u'ip', u'keyfile', u'certfile',
251 251 u'notebook-dir']
252 252
253 253 #-----------------------------------------------------------------------------
254 254 # NotebookApp
255 255 #-----------------------------------------------------------------------------
256 256
257 257 class NotebookApp(BaseIPythonApplication):
258 258
259 259 name = 'ipython-notebook'
260 260 default_config_file_name='ipython_notebook_config.py'
261 261
262 262 description = """
263 263 The IPython HTML Notebook.
264 264
265 265 This launches a Tornado based HTML Notebook Server that serves up an
266 266 HTML5/Javascript Notebook client.
267 267 """
268 268 examples = _examples
269 269
270 270 classes = IPythonConsoleApp.classes + [MappingKernelManager, NotebookManager,
271 271 FileNotebookManager]
272 272 flags = Dict(flags)
273 273 aliases = Dict(aliases)
274 274
275 275 kernel_argv = List(Unicode)
276 276
277 277 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
278 278 default_value=logging.INFO,
279 279 config=True,
280 280 help="Set the log level by value or name.")
281 281
282 282 # create requested profiles by default, if they don't exist:
283 283 auto_create = Bool(True)
284 284
285 285 # file to be opened in the notebook server
286 286 file_to_run = Unicode('')
287 287
288 288 # Network related information.
289 289
290 290 ip = Unicode(LOCALHOST, config=True,
291 291 help="The IP address the notebook server will listen on."
292 292 )
293 293
294 294 def _ip_changed(self, name, old, new):
295 295 if new == u'*': self.ip = u''
296 296
297 297 port = Integer(8888, config=True,
298 298 help="The port the notebook server will listen on."
299 299 )
300 300 port_retries = Integer(50, config=True,
301 301 help="The number of additional ports to try if the specified port is not available."
302 302 )
303 303
304 304 certfile = Unicode(u'', config=True,
305 305 help="""The full path to an SSL/TLS certificate file."""
306 306 )
307 307
308 308 keyfile = Unicode(u'', config=True,
309 309 help="""The full path to a private key file for usage with SSL/TLS."""
310 310 )
311 311
312 312 password = Unicode(u'', config=True,
313 313 help="""Hashed password to use for web authentication.
314 314
315 315 To generate, type in a python/IPython shell:
316 316
317 317 from IPython.lib import passwd; passwd()
318 318
319 319 The string should be of the form type:salt:hashed-password.
320 320 """
321 321 )
322 322
323 323 open_browser = Bool(True, config=True,
324 324 help="""Whether to open in a browser after starting.
325 325 The specific browser used is platform dependent and
326 326 determined by the python standard library `webbrowser`
327 327 module, unless it is overridden using the --browser
328 328 (NotebookApp.browser) configuration option.
329 329 """)
330 330
331 331 browser = Unicode(u'', config=True,
332 332 help="""Specify what command to use to invoke a web
333 333 browser when opening the notebook. If not specified, the
334 334 default browser will be determined by the `webbrowser`
335 335 standard library module, which allows setting of the
336 336 BROWSER environment variable to override it.
337 337 """)
338 338
339 339 read_only = Bool(False, config=True,
340 340 help="Whether to prevent editing/execution of notebooks."
341 341 )
342 342
343 343 webapp_settings = Dict(config=True,
344 344 help="Supply overrides for the tornado.web.Application that the "
345 345 "IPython notebook uses.")
346 346
347 347 enable_mathjax = Bool(True, config=True,
348 348 help="""Whether to enable MathJax for typesetting math/TeX
349 349
350 350 MathJax is the javascript library IPython uses to render math/LaTeX. It is
351 351 very large, so you may want to disable it if you have a slow internet
352 352 connection, or for offline use of the notebook.
353 353
354 354 When disabled, equations etc. will appear as their untransformed TeX source.
355 355 """
356 356 )
357 357 def _enable_mathjax_changed(self, name, old, new):
358 358 """set mathjax url to empty if mathjax is disabled"""
359 359 if not new:
360 360 self.mathjax_url = u''
361 361
362 362 base_project_url = Unicode('/', config=True,
363 363 help='''The base URL for the notebook server''')
364 364 base_kernel_url = Unicode('/', config=True,
365 365 help='''The base URL for the kernel server''')
366 366 websocket_host = Unicode("", config=True,
367 367 help="""The hostname for the websocket server."""
368 368 )
369 369
370 370 extra_static_paths = List(Unicode, config=True,
371 371 help="""Extra paths to search for serving static files.
372 372
373 373 This allows adding javascript/css to be available from the notebook server machine,
374 374 or overriding individual files in the IPython"""
375 375 )
376 376 def _extra_static_paths_default(self):
377 377 return [os.path.join(self.profile_dir.location, 'static')]
378 378
379 379 @property
380 380 def static_file_path(self):
381 381 """return extra paths + the default location"""
382 382 return self.extra_static_paths + [os.path.join(os.path.dirname(__file__), "static")]
383 383
384 384 mathjax_url = Unicode("", config=True,
385 385 help="""The url for MathJax.js."""
386 386 )
387 387 def _mathjax_url_default(self):
388 388 if not self.enable_mathjax:
389 389 return u''
390 390 static_url_prefix = self.webapp_settings.get("static_url_prefix",
391 391 "/static/")
392 392 try:
393 393 mathjax = filefind(os.path.join('mathjax', 'MathJax.js'), self.static_file_path)
394 394 except IOError:
395 395 if self.certfile:
396 396 # HTTPS: load from Rackspace CDN, because SSL certificate requires it
397 397 base = u"https://c328740.ssl.cf1.rackcdn.com"
398 398 else:
399 399 base = u"http://cdn.mathjax.org"
400 400
401 401 url = base + u"/mathjax/latest/MathJax.js"
402 402 self.log.info("Using MathJax from CDN: %s", url)
403 403 return url
404 404 else:
405 405 self.log.info("Using local MathJax from %s" % mathjax)
406 406 return static_url_prefix+u"mathjax/MathJax.js"
407 407
408 408 def _mathjax_url_changed(self, name, old, new):
409 409 if new and not self.enable_mathjax:
410 410 # enable_mathjax=False overrides mathjax_url
411 411 self.mathjax_url = u''
412 412 else:
413 413 self.log.info("Using MathJax: %s", new)
414 414
415 415 notebook_manager_class = DottedObjectName('IPython.frontend.html.notebook.filenbmanager.FileNotebookManager',
416 416 config=True,
417 417 help='The notebook manager class to use.')
418 418
419 419 def parse_command_line(self, argv=None):
420 420 super(NotebookApp, self).parse_command_line(argv)
421 421 if argv is None:
422 422 argv = sys.argv[1:]
423 423
424 424 # Scrub frontend-specific flags
425 425 self.kernel_argv = swallow_argv(argv, notebook_aliases, notebook_flags)
426 426 # Kernel should inherit default config file from frontend
427 427 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
428 428
429 429 if self.extra_args:
430 430 f = os.path.abspath(self.extra_args[0])
431 431 if os.path.isdir(f):
432 432 nbdir = f
433 433 else:
434 434 self.file_to_run = f
435 435 nbdir = os.path.dirname(f)
436 436 self.config.NotebookManager.notebook_dir = nbdir
437 437
438 438 def init_configurables(self):
439 439 # force Session default to be secure
440 440 default_secure(self.config)
441 441 self.kernel_manager = MappingKernelManager(
442 442 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
443 443 connection_dir = self.profile_dir.security_dir,
444 444 )
445 445 kls = import_item(self.notebook_manager_class)
446 446 self.notebook_manager = kls(config=self.config, log=self.log)
447 447 self.notebook_manager.log_info()
448 448 self.notebook_manager.load_notebook_names()
449 449 self.cluster_manager = ClusterManager(config=self.config, log=self.log)
450 450 self.cluster_manager.update_profiles()
451 451
452 452 def init_logging(self):
453 453 # This prevents double log messages because tornado use a root logger that
454 454 # self.log is a child of. The logging module dipatches log messages to a log
455 455 # and all of its ancenstors until propagate is set to False.
456 456 self.log.propagate = False
457 457
458 458 def init_webapp(self):
459 459 """initialize tornado webapp and httpserver"""
460 460 self.web_app = NotebookWebApplication(
461 461 self, self.kernel_manager, self.notebook_manager,
462 462 self.cluster_manager, self.log,
463 463 self.base_project_url, self.webapp_settings
464 464 )
465 465 if self.certfile:
466 466 ssl_options = dict(certfile=self.certfile)
467 467 if self.keyfile:
468 468 ssl_options['keyfile'] = self.keyfile
469 469 else:
470 470 ssl_options = None
471 471 self.web_app.password = self.password
472 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):
474 self.log.critical('WARNING: the notebook server is listening on all IP addresses '
475 'but not using any encryption or authentication. This is highly '
476 'insecure and not recommended.')
477
473 if not self.ip:
474 warning = "WARNING: The notebook server is listening on all IP addresses"
475 if ssl_options is None:
476 self.log.critical(warning + " and not using encryption. This"
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 481 success = None
479 482 for port in random_ports(self.port, self.port_retries+1):
480 483 try:
481 484 self.http_server.listen(port, self.ip)
482 485 except socket.error as e:
483 486 if e.errno != errno.EADDRINUSE:
484 487 raise
485 488 self.log.info('The port %i is already in use, trying another random port.' % port)
486 489 else:
487 490 self.port = port
488 491 success = True
489 492 break
490 493 if not success:
491 494 self.log.critical('ERROR: the notebook server could not be started because '
492 495 'no available port could be found.')
493 496 self.exit(1)
494 497
495 498 def init_signal(self):
496 499 # FIXME: remove this check when pyzmq dependency is >= 2.1.11
497 500 # safely extract zmq version info:
498 501 try:
499 502 zmq_v = zmq.pyzmq_version_info()
500 503 except AttributeError:
501 504 zmq_v = [ int(n) for n in re.findall(r'\d+', zmq.__version__) ]
502 505 if 'dev' in zmq.__version__:
503 506 zmq_v.append(999)
504 507 zmq_v = tuple(zmq_v)
505 508 if zmq_v >= (2,1,9) and not sys.platform.startswith('win'):
506 509 # This won't work with 2.1.7 and
507 510 # 2.1.9-10 will log ugly 'Interrupted system call' messages,
508 511 # but it will work
509 512 signal.signal(signal.SIGINT, self._handle_sigint)
510 513 signal.signal(signal.SIGTERM, self._signal_stop)
511 514
512 515 def _handle_sigint(self, sig, frame):
513 516 """SIGINT handler spawns confirmation dialog"""
514 517 # register more forceful signal handler for ^C^C case
515 518 signal.signal(signal.SIGINT, self._signal_stop)
516 519 # request confirmation dialog in bg thread, to avoid
517 520 # blocking the App
518 521 thread = threading.Thread(target=self._confirm_exit)
519 522 thread.daemon = True
520 523 thread.start()
521 524
522 525 def _restore_sigint_handler(self):
523 526 """callback for restoring original SIGINT handler"""
524 527 signal.signal(signal.SIGINT, self._handle_sigint)
525 528
526 529 def _confirm_exit(self):
527 530 """confirm shutdown on ^C
528 531
529 532 A second ^C, or answering 'y' within 5s will cause shutdown,
530 533 otherwise original SIGINT handler will be restored.
531 534
532 535 This doesn't work on Windows.
533 536 """
534 537 # FIXME: remove this delay when pyzmq dependency is >= 2.1.11
535 538 time.sleep(0.1)
536 539 sys.stdout.write("Shutdown Notebook Server (y/[n])? ")
537 540 sys.stdout.flush()
538 541 r,w,x = select.select([sys.stdin], [], [], 5)
539 542 if r:
540 543 line = sys.stdin.readline()
541 544 if line.lower().startswith('y'):
542 545 self.log.critical("Shutdown confirmed")
543 546 ioloop.IOLoop.instance().stop()
544 547 return
545 548 else:
546 549 print "No answer for 5s:",
547 550 print "resuming operation..."
548 551 # no answer, or answer is no:
549 552 # set it back to original SIGINT handler
550 553 # use IOLoop.add_callback because signal.signal must be called
551 554 # from main thread
552 555 ioloop.IOLoop.instance().add_callback(self._restore_sigint_handler)
553 556
554 557 def _signal_stop(self, sig, frame):
555 558 self.log.critical("received signal %s, stopping", sig)
556 559 ioloop.IOLoop.instance().stop()
557 560
558 561 @catch_config_error
559 562 def initialize(self, argv=None):
560 563 self.init_logging()
561 564 super(NotebookApp, self).initialize(argv)
562 565 self.init_configurables()
563 566 self.init_webapp()
564 567 self.init_signal()
565 568
566 569 def cleanup_kernels(self):
567 570 """shutdown all kernels
568 571
569 572 The kernels will shutdown themselves when this process no longer exists,
570 573 but explicit shutdown allows the KernelManagers to cleanup the connection files.
571 574 """
572 575 self.log.info('Shutting down kernels')
573 576 km = self.kernel_manager
574 577 # copy list, since shutdown_kernel deletes keys
575 578 for kid in list(km.kernel_ids):
576 579 km.shutdown_kernel(kid)
577 580
578 581 def start(self):
579 582 ip = self.ip if self.ip else '[all ip addresses on your system]'
580 583 proto = 'https' if self.certfile else 'http'
581 584 info = self.log.info
582 585 info("The IPython Notebook is running at: %s://%s:%i%s" %
583 586 (proto, ip, self.port,self.base_project_url) )
584 587 info("Use Control-C to stop this server and shut down all kernels.")
585 588
586 589 if self.open_browser or self.file_to_run:
587 590 ip = self.ip or '127.0.0.1'
588 591 try:
589 592 browser = webbrowser.get(self.browser or None)
590 593 except webbrowser.Error as e:
591 594 self.log.warn('No web browser found: %s.' % e)
592 595 browser = None
593 596
594 597 if self.file_to_run:
595 598 name, _ = os.path.splitext(os.path.basename(self.file_to_run))
596 599 url = self.notebook_manager.rev_mapping.get(name, '')
597 600 else:
598 601 url = ''
599 602 if browser:
600 603 b = lambda : browser.open("%s://%s:%i%s%s" % (proto, ip,
601 604 self.port, self.base_project_url, url), new=2)
602 605 threading.Thread(target=b).start()
603 606 try:
604 607 ioloop.IOLoop.instance().start()
605 608 except KeyboardInterrupt:
606 609 info("Interrupted...")
607 610 finally:
608 611 self.cleanup_kernels()
609 612
610 613
611 614 #-----------------------------------------------------------------------------
612 615 # Main entry point
613 616 #-----------------------------------------------------------------------------
614 617
615 618 def launch_new_instance():
616 619 app = NotebookApp.instance()
617 620 app.initialize()
618 621 app.start()
619 622
@@ -1,220 +1,218 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Cell
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var utils = IPython.utils;
15 15
16 16
17 17 var Cell = function () {
18 18 this.placeholder = this.placeholder || '';
19 19 this.read_only = false;
20 20 this.selected = false;
21 21 this.element = null;
22 22 this.metadata = {};
23 23 // load this from metadata later ?
24 24 this.user_highlight == 'auto';
25 25 this.create_element();
26 26 if (this.element !== null) {
27 27 this.element.data("cell", this);
28 28 this.bind_events();
29 29 }
30 30 this.cell_id = utils.uuid();
31 31 };
32 32
33 33
34 34 // Subclasses must implement create_element.
35 35 Cell.prototype.create_element = function () {};
36 36
37 37
38 38 Cell.prototype.bind_events = function () {
39 39 var that = this;
40 40 // We trigger events so that Cell doesn't have to depend on Notebook.
41 41 that.element.click(function (event) {
42 42 if (that.selected === false) {
43 43 $([IPython.events]).trigger('select.Cell', {'cell':that});
44 44 }
45 45 });
46 46 that.element.focusin(function (event) {
47 47 if (that.selected === false) {
48 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 53 Cell.prototype.typeset = function () {
56 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 60 Cell.prototype.select = function () {
63 61 this.element.addClass('ui-widget-content ui-corner-all');
64 62 this.selected = true;
65 63 };
66 64
67 65
68 66 Cell.prototype.unselect = function () {
69 67 this.element.removeClass('ui-widget-content ui-corner-all');
70 68 this.selected = false;
71 69 };
72 70
73 71
74 72 Cell.prototype.get_text = function () {
75 73 };
76 74
77 75
78 76 Cell.prototype.set_text = function (text) {
79 77 };
80 78
81 79
82 80 Cell.prototype.refresh = function () {
83 81 this.code_mirror.refresh();
84 82 };
85 83
86 84
87 85 Cell.prototype.edit = function () {
88 86 };
89 87
90 88
91 89 Cell.prototype.render = function () {
92 90 };
93 91
94 92
95 93 Cell.prototype.toJSON = function () {
96 94 var data = {};
97 95 data.metadata = this.metadata;
98 96 return data;
99 97 };
100 98
101 99
102 100 Cell.prototype.fromJSON = function (data) {
103 101 if (data.metadata !== undefined) {
104 102 this.metadata = data.metadata;
105 103 }
106 104 };
107 105
108 106
109 107 Cell.prototype.is_splittable = function () {
110 108 return true;
111 109 };
112 110
113 111
114 112 Cell.prototype.get_pre_cursor = function () {
115 113 var cursor = this.code_mirror.getCursor();
116 114 var text = this.code_mirror.getRange({line:0,ch:0}, cursor);
117 115 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
118 116 return text;
119 117 }
120 118
121 119
122 120 Cell.prototype.get_post_cursor = function () {
123 121 var cursor = this.code_mirror.getCursor();
124 122 var last_line_num = this.code_mirror.lineCount()-1;
125 123 var last_line_len = this.code_mirror.getLine(last_line_num).length;
126 124 var end = {line:last_line_num, ch:last_line_len}
127 125 var text = this.code_mirror.getRange(cursor, end);
128 126 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
129 127 return text;
130 128 };
131 129
132 130
133 131 Cell.prototype.grow = function(element) {
134 132 // Grow the cell by hand. This is used upon reloading from JSON, when the
135 133 // autogrow handler is not called.
136 134 var dom = element.get(0);
137 135 var lines_count = 0;
138 136 // modified split rule from
139 137 // http://stackoverflow.com/questions/2035910/how-to-get-the-number-of-lines-in-a-textarea/2036424#2036424
140 138 var lines = dom.value.split(/\r|\r\n|\n/);
141 139 lines_count = lines.length;
142 140 if (lines_count >= 1) {
143 141 dom.rows = lines_count;
144 142 } else {
145 143 dom.rows = 1;
146 144 }
147 145 };
148 146
149 147
150 148 Cell.prototype.toggle_line_numbers = function () {
151 149 if (this.code_mirror.getOption('lineNumbers') == false) {
152 150 this.code_mirror.setOption('lineNumbers', true);
153 151 } else {
154 152 this.code_mirror.setOption('lineNumbers', false);
155 153 }
156 154 this.code_mirror.refresh();
157 155 };
158 156
159 157 Cell.prototype.force_highlight = function(mode) {
160 158 this.user_highlight = mode;
161 159 this.auto_highlight();
162 160 };
163 161
164 162 Cell.prototype._auto_highlight = function (modes) {
165 163 //Here we handle manually selected modes
166 164 if( this.user_highlight != undefined && this.user_highlight != 'auto' )
167 165 {
168 166 var mode = this.user_highlight;
169 167 CodeMirror.autoLoadMode(this.code_mirror, mode);
170 168 this.code_mirror.setOption('mode', mode);
171 169 return;
172 170 }
173 171 var first_line = this.code_mirror.getLine(0);
174 172 // loop on every pairs
175 173 for( var mode in modes) {
176 174 var regs = modes[mode]['reg'];
177 175 // only one key every time but regexp can't be keys...
178 176 for(var reg in regs ) {
179 177 // here we handle non magic_modes
180 178 if(first_line.match(regs[reg]) != null) {
181 179 if (mode.search('magic_') != 0) {
182 180 this.code_mirror.setOption('mode',mode);
183 181 CodeMirror.autoLoadMode(this.code_mirror, mode);
184 182 return;
185 183 }
186 184 var open = modes[mode]['open']|| "%%";
187 185 var close = modes[mode]['close']|| "%%end";
188 186 var mmode = mode;
189 187 mode = mmode.substr(6);
190 188 CodeMirror.autoLoadMode(this.code_mirror, mode);
191 189 // create on the fly a mode that swhitch between
192 190 // plain/text and smth else otherwise `%%` is
193 191 // source of some highlight issues.
194 192 // we use patchedGetMode to circumvent a bug in CM
195 193 CodeMirror.defineMode(mmode , function(config) {
196 194 return CodeMirror.multiplexingMode(
197 195 CodeMirror.patchedGetMode(config, 'text/plain'),
198 196 // always set someting on close
199 197 {open: open, close: close,
200 198 mode: CodeMirror.patchedGetMode(config, mode),
201 199 delimStyle: "delimit"
202 200 }
203 201 );
204 202 });
205 203 this.code_mirror.setOption('mode', mmode);
206 204 return;
207 205 }
208 206 }
209 207 }
210 208 // fallback on default (python)
211 209 var default_mode = this.default_mode || 'text/plain';
212 210 this.code_mirror.setOption('mode', default_mode);
213 211 };
214 212
215 213 IPython.Cell = Cell;
216 214
217 215 return IPython;
218 216
219 217 }(IPython));
220 218
@@ -1,343 +1,344 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // CodeCell
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13 "use strict";
14 14
15 15 var utils = IPython.utils;
16 16 var key = IPython.utils.keycodes;
17 17 CodeMirror.modeURL = "/static/codemirror/mode/%N/%N.js";
18 18
19 19 var CodeCell = function (kernel) {
20 20 // The kernel doesn't have to be set at creation time, in that case
21 21 // it will be null and set_kernel has to be called later.
22 22 this.kernel = kernel || null;
23 23 this.code_mirror = null;
24 24 this.input_prompt_number = null;
25 25 this.tooltip_on_tab = true;
26 26 this.collapsed = false;
27 27 this.default_mode = 'python';
28 28 IPython.Cell.apply(this, arguments);
29 29
30 30 var that = this;
31 31 this.element.focusout(
32 32 function() { that.auto_highlight(); }
33 33 );
34 34 };
35 35
36 36
37 37 CodeCell.prototype = new IPython.Cell();
38 38
39 39
40 40 CodeCell.prototype.auto_highlight = function () {
41 41 this._auto_highlight(IPython.config.cell_magic_highlight)
42 42 };
43 43
44 44 CodeCell.prototype.create_element = function () {
45 45 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
46 46 cell.attr('tabindex','2');
47 47 var input = $('<div></div>').addClass('input hbox');
48 48 input.append($('<div/>').addClass('prompt input_prompt'));
49 49 var input_area = $('<div/>').addClass('input_area box-flex1');
50 50 this.code_mirror = CodeMirror(input_area.get(0), {
51 51 indentUnit : 4,
52 52 mode: 'python',
53 53 theme: 'ipython',
54 54 readOnly: this.read_only,
55 55 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess",'Backspace':"delSpaceToPrevTabStop"},
56 56 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
57 57 });
58 58 input.append(input_area);
59 59 var output = $('<div></div>');
60 60 cell.append(input).append(output);
61 61 this.element = cell;
62 62 this.output_area = new IPython.OutputArea(output, true);
63 63
64 64 // construct a completer only if class exist
65 65 // otherwise no print view
66 66 if (IPython.Completer !== undefined)
67 67 {
68 68 this.completer = new IPython.Completer(this);
69 69 }
70 70 };
71 71
72 72 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
73 73 // This method gets called in CodeMirror's onKeyDown/onKeyPress
74 74 // handlers and is used to provide custom key handling. Its return
75 75 // value is used to determine if CodeMirror should ignore the event:
76 76 // true = ignore, false = don't ignore.
77 77
78 78 if (this.read_only){
79 79 return false;
80 80 }
81 81
82 82 var that = this;
83 83 // whatever key is pressed, first, cancel the tooltip request before
84 84 // they are sent, and remove tooltip if any, except for tab again
85 85 if (event.type === 'keydown' && event.which != key.TAB ) {
86 86 IPython.tooltip.remove_and_cancel_tooltip();
87 87 };
88 88
89 89 var cur = editor.getCursor();
90 90 if (event.keyCode === key.ENTER){
91 91 this.auto_highlight();
92 92 }
93 93
94 94 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
95 95 // Always ignore shift-enter in CodeMirror as we handle it.
96 96 return true;
97 97 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
98 98 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
99 99 // browser and keyboard layout !
100 100 // Pressing '(' , request tooltip, don't forget to reappend it
101 101 IPython.tooltip.pending(that);
102 102 } else if (event.which === key.UPARROW && event.type === 'keydown') {
103 103 // If we are not at the top, let CM handle the up arrow and
104 104 // prevent the global keydown handler from handling it.
105 105 if (!that.at_top()) {
106 106 event.stop();
107 107 return false;
108 108 } else {
109 109 return true;
110 110 };
111 111 } else if (event.which === key.ESC) {
112 112 IPython.tooltip.remove_and_cancel_tooltip(true);
113 113 return true;
114 114 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
115 115 // If we are not at the bottom, let CM handle the down arrow and
116 116 // prevent the global keydown handler from handling it.
117 117 if (!that.at_bottom()) {
118 118 event.stop();
119 119 return false;
120 120 } else {
121 121 return true;
122 122 };
123 123 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
124 124 // Tab completion.
125 125 //Do not trim here because of tooltip
126 if (editor.somethingSelected()){return false}
126 127 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
127 128 if (pre_cursor.trim() === "") {
128 129 // Don't autocomplete if the part of the line before the cursor
129 130 // is empty. In this case, let CodeMirror handle indentation.
130 131 return false;
131 132 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && that.tooltip_on_tab ) {
132 133 IPython.tooltip.request(that);
133 134 // Prevent the event from bubbling up.
134 135 event.stop();
135 136 // Prevent CodeMirror from handling the tab.
136 137 return true;
137 138 } else {
138 139 event.stop();
139 140 this.completer.startCompletion();
140 141 return true;
141 142 };
142 143 } else {
143 144 // keypress/keyup also trigger on TAB press, and we don't want to
144 145 // use those to disable tab completion.
145 146 return false;
146 147 };
147 148 return false;
148 149 };
149 150
150 151
151 152 // Kernel related calls.
152 153
153 154 CodeCell.prototype.set_kernel = function (kernel) {
154 155 this.kernel = kernel;
155 156 }
156 157
157 158
158 159 CodeCell.prototype.execute = function () {
159 160 this.output_area.clear_output(true, true, true);
160 161 this.set_input_prompt('*');
161 162 this.element.addClass("running");
162 163 var callbacks = {
163 164 'execute_reply': $.proxy(this._handle_execute_reply, this),
164 165 'output': $.proxy(this.output_area.handle_output, this.output_area),
165 166 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
166 167 'set_next_input': $.proxy(this._handle_set_next_input, this)
167 168 };
168 169 var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false});
169 170 };
170 171
171 172
172 173 CodeCell.prototype._handle_execute_reply = function (content) {
173 174 this.set_input_prompt(content.execution_count);
174 175 this.element.removeClass("running");
175 176 $([IPython.events]).trigger('set_dirty.Notebook', {'value': true});
176 177 }
177 178
178 179 CodeCell.prototype._handle_set_next_input = function (text) {
179 180 var data = {'cell': this, 'text': text}
180 181 $([IPython.events]).trigger('set_next_input.Notebook', data);
181 182 }
182 183
183 184 // Basic cell manipulation.
184 185
185 186 CodeCell.prototype.select = function () {
186 187 IPython.Cell.prototype.select.apply(this);
187 188 this.code_mirror.refresh();
188 189 this.code_mirror.focus();
189 190 this.auto_highlight();
190 191 // We used to need an additional refresh() after the focus, but
191 192 // it appears that this has been fixed in CM. This bug would show
192 193 // up on FF when a newly loaded markdown cell was edited.
193 194 };
194 195
195 196
196 197 CodeCell.prototype.select_all = function () {
197 198 var start = {line: 0, ch: 0};
198 199 var nlines = this.code_mirror.lineCount();
199 200 var last_line = this.code_mirror.getLine(nlines-1);
200 201 var end = {line: nlines-1, ch: last_line.length};
201 202 this.code_mirror.setSelection(start, end);
202 203 };
203 204
204 205
205 206 CodeCell.prototype.collapse = function () {
206 207 this.collapsed = true;
207 208 this.output_area.collapse();
208 209 };
209 210
210 211
211 212 CodeCell.prototype.expand = function () {
212 213 this.collapsed = false;
213 214 this.output_area.expand();
214 215 };
215 216
216 217
217 218 CodeCell.prototype.toggle_output = function () {
218 219 this.collapsed = Boolean(1 - this.collapsed);
219 220 this.output_area.toggle_output();
220 221 };
221 222
222 223
223 224 CodeCell.prototype.toggle_output_scroll = function () {
224 225 this.output_area.toggle_scroll();
225 226 };
226 227
227 228
228 229
229 230
230 231
231 232 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
232 233 var ns = prompt_value || "&nbsp;";
233 234 return 'In&nbsp;[' + ns + ']:'
234 235 };
235 236
236 237 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
237 238 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
238 239 for(var i=1; i < lines_number; i++){html.push(['...:'])};
239 240 return html.join('</br>')
240 241 };
241 242
242 243 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
243 244
244 245
245 246 CodeCell.prototype.set_input_prompt = function (number) {
246 247 var nline = 1
247 248 if( this.code_mirror != undefined) {
248 249 nline = this.code_mirror.lineCount();
249 250 }
250 251 this.input_prompt_number = number;
251 252 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
252 253 this.element.find('div.input_prompt').html(prompt_html);
253 254 };
254 255
255 256
256 257 CodeCell.prototype.clear_input = function () {
257 258 this.code_mirror.setValue('');
258 259 };
259 260
260 261
261 262 CodeCell.prototype.get_text = function () {
262 263 return this.code_mirror.getValue();
263 264 };
264 265
265 266
266 267 CodeCell.prototype.set_text = function (code) {
267 268 return this.code_mirror.setValue(code);
268 269 };
269 270
270 271
271 272 CodeCell.prototype.at_top = function () {
272 273 var cursor = this.code_mirror.getCursor();
273 274 if (cursor.line === 0 && cursor.ch === 0) {
274 275 return true;
275 276 } else {
276 277 return false;
277 278 }
278 279 };
279 280
280 281
281 282 CodeCell.prototype.at_bottom = function () {
282 283 var cursor = this.code_mirror.getCursor();
283 284 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
284 285 return true;
285 286 } else {
286 287 return false;
287 288 }
288 289 };
289 290
290 291
291 292 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
292 293 this.output_area.clear_output(stdout, stderr, other);
293 294 };
294 295
295 296
296 297 // JSON serialization
297 298
298 299 CodeCell.prototype.fromJSON = function (data) {
299 300 IPython.Cell.prototype.fromJSON.apply(this, arguments);
300 301 if (data.cell_type === 'code') {
301 302 if (data.input !== undefined) {
302 303 this.set_text(data.input);
303 304 // make this value the starting point, so that we can only undo
304 305 // to this state, instead of a blank cell
305 306 this.code_mirror.clearHistory();
306 307 this.auto_highlight();
307 308 }
308 309 if (data.prompt_number !== undefined) {
309 310 this.set_input_prompt(data.prompt_number);
310 311 } else {
311 312 this.set_input_prompt();
312 313 };
313 314 this.output_area.fromJSON(data.outputs);
314 315 if (data.collapsed !== undefined) {
315 316 if (data.collapsed) {
316 317 this.collapse();
317 318 } else {
318 319 this.expand();
319 320 };
320 321 };
321 322 };
322 323 };
323 324
324 325
325 326 CodeCell.prototype.toJSON = function () {
326 327 var data = IPython.Cell.prototype.toJSON.apply(this);
327 328 data.input = this.get_text();
328 329 data.cell_type = 'code';
329 330 if (this.input_prompt_number) {
330 331 data.prompt_number = this.input_prompt_number;
331 332 };
332 333 var outputs = this.output_area.toJSON();
333 334 data.outputs = outputs;
334 335 data.language = 'python';
335 336 data.collapsed = this.collapsed;
336 337 return data;
337 338 };
338 339
339 340
340 341 IPython.CodeCell = CodeCell;
341 342
342 343 return IPython;
343 344 }(IPython));
@@ -1,391 +1,394 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Kernel
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var utils = IPython.utils;
15 15
16 16 // Initialization and connection.
17 17
18 18 var Kernel = function (base_url) {
19 19 this.kernel_id = null;
20 20 this.shell_channel = null;
21 21 this.iopub_channel = null;
22 22 this.base_url = base_url;
23 23 this.running = false;
24 24 this.username = "username";
25 25 this.session_id = utils.uuid();
26 26 this._msg_callbacks = {};
27 27
28 28 if (typeof(WebSocket) !== 'undefined') {
29 29 this.WebSocket = WebSocket;
30 30 } else if (typeof(MozWebSocket) !== 'undefined') {
31 31 this.WebSocket = MozWebSocket;
32 32 } else {
33 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 38 Kernel.prototype._get_msg = function (msg_type, content) {
39 39 var msg = {
40 40 header : {
41 41 msg_id : utils.uuid(),
42 42 username : this.username,
43 43 session : this.session_id,
44 44 msg_type : msg_type
45 45 },
46 46 metadata : {},
47 47 content : content,
48 48 parent_header : {}
49 49 };
50 50 return msg;
51 51 };
52 52
53 53 Kernel.prototype.start = function (notebook_id) {
54 54 var that = this;
55 55 if (!this.running) {
56 56 var qs = $.param({notebook:notebook_id});
57 57 var url = this.base_url + '?' + qs;
58 58 $.post(url,
59 59 $.proxy(that._kernel_started,that),
60 60 'json'
61 61 );
62 62 };
63 63 };
64 64
65 65
66 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 68 var that = this;
69 69 if (this.running) {
70 70 this.stop_channels();
71 71 var url = this.kernel_url + "/restart";
72 72 $.post(url,
73 73 $.proxy(that._kernel_started, that),
74 74 'json'
75 75 );
76 76 };
77 77 };
78 78
79 79
80 80 Kernel.prototype._kernel_started = function (json) {
81 81 console.log("Kernel started: ", json.kernel_id);
82 82 this.running = true;
83 83 this.kernel_id = json.kernel_id;
84 84 this.ws_url = json.ws_url;
85 85 this.kernel_url = this.base_url + "/" + this.kernel_id;
86 86 this.start_channels();
87 87 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply,this);
88 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 93 Kernel.prototype._websocket_closed = function(ws_url, early){
93 94 var msg;
94 95 var parent_item = $('body');
95 96 if (early) {
96 97 msg = "Websocket connection to " + ws_url + " could not be established." +
97 98 " You will NOT be able to run code." +
98 99 " Your browser may not be compatible with the websocket version in the server," +
99 100 " or if the url does not look right, there could be an error in the" +
100 101 " server's configuration.";
101 102 } else {
102 103 IPython.notification_area.widget('kernel').set_message('Reconnecting Websockets', 1000);
103 104 this.start_channels();
104 105 return;
105 106 }
106 107 var dialog = $('<div/>');
107 108 dialog.html(msg);
108 109 parent_item.append(dialog);
109 110 dialog.dialog({
110 111 resizable: false,
111 112 modal: true,
112 113 title: "Websocket closed",
113 114 closeText: "",
114 115 close: function(event, ui) {$(this).dialog('destroy').remove();},
115 116 buttons : {
116 117 "OK": function () {
117 118 $(this).dialog('close');
118 119 }
119 120 }
120 121 });
121 122
122 123 };
123 124
124 125 Kernel.prototype.start_channels = function () {
125 126 var that = this;
126 127 this.stop_channels();
127 128 var ws_url = this.ws_url + this.kernel_url;
128 129 console.log("Starting WS:", ws_url);
129 130 this.shell_channel = new this.WebSocket(ws_url + "/shell");
130 131 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
131 132 send_cookie = function(){
132 133 this.send(document.cookie);
133 134 };
134 135 var already_called_onclose = false; // only alert once
135 136 ws_closed_early = function(evt){
136 137 if (already_called_onclose){
137 138 return;
138 139 }
139 140 already_called_onclose = true;
140 141 if ( ! evt.wasClean ){
141 142 that._websocket_closed(ws_url, true);
142 143 }
143 144 };
144 145 ws_closed_late = function(evt){
145 146 if (already_called_onclose){
146 147 return;
147 148 }
148 149 already_called_onclose = true;
149 150 if ( ! evt.wasClean ){
150 151 that._websocket_closed(ws_url, false);
151 152 }
152 153 };
153 154 this.shell_channel.onopen = send_cookie;
154 155 this.shell_channel.onclose = ws_closed_early;
155 156 this.iopub_channel.onopen = send_cookie;
156 157 this.iopub_channel.onclose = ws_closed_early;
157 158 // switch from early-close to late-close message after 1s
158 159 setTimeout(function(){
159 160 that.shell_channel.onclose = ws_closed_late;
160 161 that.iopub_channel.onclose = ws_closed_late;
161 162 }, 1000);
162 163 };
163 164
164 165
165 166 Kernel.prototype.stop_channels = function () {
166 167 if (this.shell_channel !== null) {
167 168 this.shell_channel.onclose = function (evt) {};
168 169 this.shell_channel.close();
169 170 this.shell_channel = null;
170 171 };
171 172 if (this.iopub_channel !== null) {
172 173 this.iopub_channel.onclose = function (evt) {};
173 174 this.iopub_channel.close();
174 175 this.iopub_channel = null;
175 176 };
176 177 };
177 178
178 179 // Main public methods.
179 180
180 181 Kernel.prototype.object_info_request = function (objname, callbacks) {
181 182 // When calling this method pass a callbacks structure of the form:
182 183 //
183 184 // callbacks = {
184 185 // 'object_info_reply': object_into_reply_callback
185 186 // }
186 187 //
187 188 // The object_info_reply_callback will be passed the content object of the
188 189 // object_into_reply message documented here:
189 190 //
190 191 // http://ipython.org/ipython-doc/dev/development/messaging.html#object-information
191 192 if(typeof(objname)!=null && objname!=null)
192 193 {
193 194 var content = {
194 195 oname : objname.toString(),
195 196 };
196 197 var msg = this._get_msg("object_info_request", content);
197 198 this.shell_channel.send(JSON.stringify(msg));
198 199 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
199 200 return msg.header.msg_id;
200 201 }
201 202 return;
202 203 }
203 204
204 205 Kernel.prototype.execute = function (code, callbacks, options) {
205 206 // The options object should contain the options for the execute call. Its default
206 207 // values are:
207 208 //
208 209 // options = {
209 210 // silent : true,
210 211 // user_variables : [],
211 212 // user_expressions : {},
212 213 // allow_stdin : false
213 214 // }
214 215 //
215 216 // When calling this method pass a callbacks structure of the form:
216 217 //
217 218 // callbacks = {
218 219 // 'execute_reply': execute_reply_callback,
219 220 // 'output': output_callback,
220 221 // 'clear_output': clear_output_callback,
221 222 // 'set_next_input': set_next_input_callback
222 223 // }
223 224 //
224 225 // The execute_reply_callback will be passed the content and metadata objects of the execute_reply
225 226 // message documented here:
226 227 //
227 228 // http://ipython.org/ipython-doc/dev/development/messaging.html#execute
228 229 //
229 230 // The output_callback will be passed msg_type ('stream','display_data','pyout','pyerr')
230 231 // of the output and the content and metadata objects of the PUB/SUB channel that contains the
231 232 // output:
232 233 //
233 234 // http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket
234 235 //
235 236 // The clear_output_callback will be passed a content object that contains
236 237 // stdout, stderr and other fields that are booleans, as well as the metadata object.
237 238 //
238 239 // The set_next_input_callback will be passed the text that should become the next
239 240 // input cell.
240 241
241 242 var content = {
242 243 code : code,
243 244 silent : true,
244 245 user_variables : [],
245 246 user_expressions : {},
246 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 251 var msg = this._get_msg("execute_request", content);
250 252 this.shell_channel.send(JSON.stringify(msg));
251 253 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
252 254 return msg.header.msg_id;
253 255 };
254 256
255 257
256 258 Kernel.prototype.complete = function (line, cursor_pos, callbacks) {
257 259 // When calling this method pass a callbacks structure of the form:
258 260 //
259 261 // callbacks = {
260 262 // 'complete_reply': complete_reply_callback
261 263 // }
262 264 //
263 265 // The complete_reply_callback will be passed the content object of the
264 266 // complete_reply message documented here:
265 267 //
266 268 // http://ipython.org/ipython-doc/dev/development/messaging.html#complete
267 269 callbacks = callbacks || {};
268 270 var content = {
269 271 text : '',
270 272 line : line,
271 273 cursor_pos : cursor_pos
272 274 };
273 275 var msg = this._get_msg("complete_request", content);
274 276 this.shell_channel.send(JSON.stringify(msg));
275 277 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
276 278 return msg.header.msg_id;
277 279 };
278 280
279 281
280 282 Kernel.prototype.interrupt = function () {
281 283 if (this.running) {
282 $([IPython.events]).trigger({type: 'status_interrupting.Kernel', kernel: this});
284 $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this});
283 285 $.post(this.kernel_url + "/interrupt");
284 286 };
285 287 };
286 288
287 289
288 290 Kernel.prototype.kill = function () {
289 291 if (this.running) {
290 292 this.running = false;
291 293 var settings = {
292 294 cache : false,
293 295 type : "DELETE"
294 296 };
295 297 $.ajax(this.kernel_url, settings);
296 298 };
297 299 };
298 300
299 301
300 302 // Reply handlers.
301 303
302 304 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
303 305 var callbacks = this._msg_callbacks[msg_id];
304 306 return callbacks;
305 307 };
306 308
307 309
308 310 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
309 311 this._msg_callbacks[msg_id] = callbacks || {};
310 312 }
311 313
312 314
313 315 Kernel.prototype._handle_shell_reply = function (e) {
314 316 reply = $.parseJSON(e.data);
317 $([IPython.events]).trigger('shell_reply.Kernel', {kernel: this, reply:reply});
315 318 var header = reply.header;
316 319 var content = reply.content;
317 320 var metadata = reply.metadata;
318 321 var msg_type = header.msg_type;
319 322 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
320 323 if (callbacks !== undefined) {
321 324 var cb = callbacks[msg_type];
322 325 if (cb !== undefined) {
323 326 cb(content, metadata);
324 327 }
325 328 };
326 329
327 330 if (content.payload !== undefined) {
328 331 var payload = content.payload || [];
329 332 this._handle_payload(callbacks, payload);
330 333 }
331 334 };
332 335
333 336
334 337 Kernel.prototype._handle_payload = function (callbacks, payload) {
335 338 var l = payload.length;
336 339 // Payloads are handled by triggering events because we don't want the Kernel
337 340 // to depend on the Notebook or Pager classes.
338 341 for (var i=0; i<l; i++) {
339 342 if (payload[i].source === 'IPython.zmq.page.page') {
340 343 var data = {'text':payload[i].text}
341 344 $([IPython.events]).trigger('open_with_text.Pager', data);
342 345 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
343 346 if (callbacks.set_next_input !== undefined) {
344 347 callbacks.set_next_input(payload[i].text)
345 348 }
346 349 }
347 350 };
348 351 };
349 352
350 353
351 354 Kernel.prototype._handle_iopub_reply = function (e) {
352 355 var reply = $.parseJSON(e.data);
353 356 var content = reply.content;
354 357 var msg_type = reply.header.msg_type;
355 358 var metadata = reply.metadata;
356 359 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
357 360 if (msg_type !== 'status' && callbacks === undefined) {
358 361 // Message not from one of this notebook's cells and there are no
359 362 // callbacks to handle it.
360 363 return;
361 364 }
362 365 var output_types = ['stream','display_data','pyout','pyerr'];
363 366 if (output_types.indexOf(msg_type) >= 0) {
364 367 var cb = callbacks['output'];
365 368 if (cb !== undefined) {
366 369 cb(msg_type, content, metadata);
367 370 }
368 371 } else if (msg_type === 'status') {
369 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 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 376 } else if (content.execution_state === 'dead') {
374 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 380 } else if (msg_type === 'clear_output') {
378 381 var cb = callbacks['clear_output'];
379 382 if (cb !== undefined) {
380 383 cb(content, metadata);
381 384 }
382 385 };
383 386 };
384 387
385 388
386 389 IPython.Kernel = Kernel;
387 390
388 391 return IPython;
389 392
390 393 }(IPython));
391 394
@@ -1,179 +1,179 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // ToolBar
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var MainToolBar = function (selector) {
15 15 this.selector = selector;
16 16 IPython.ToolBar.apply(this, arguments);
17 17 this.construct();
18 18 this.add_drop_down_list();
19 19 this.bind_events();
20 20 };
21 21
22 22 MainToolBar.prototype = new IPython.ToolBar();
23 23
24 24 MainToolBar.prototype.construct = function () {
25 25 this.add_buttons_group([
26 26 {
27 27 id : 'save_b',
28 28 label : 'Save',
29 29 icon : 'ui-icon-disk',
30 30 callback : function () {
31 31 IPython.notebook.save_notebook();
32 32 }
33 33 }
34 34 ]);
35 35 this.add_buttons_group([
36 36 {
37 37 id : 'cut_b',
38 38 label : 'Cut Cell',
39 39 icon : 'ui-icon-scissors',
40 40 callback : function () {
41 41 IPython.notebook.cut_cell();
42 42 }
43 43 },
44 44 {
45 45 id : 'copy_b',
46 46 label : 'Copy Cell',
47 47 icon : 'ui-icon-copy',
48 48 callback : function () {
49 49 IPython.notebook.copy_cell();
50 50 }
51 51 },
52 52 {
53 53 id : 'paste_b',
54 label : 'Paste Cell',
54 label : 'Paste Cell Below',
55 55 icon : 'ui-icon-clipboard',
56 56 callback : function () {
57 IPython.notebook.paste_cell();
57 IPython.notebook.paste_cell_below();
58 58 }
59 59 }
60 60 ],'cut_copy_paste');
61 61
62 62 this.add_buttons_group([
63 63 {
64 64 id : 'move_up_b',
65 65 label : 'Move Cell Up',
66 66 icon : 'ui-icon-arrowthick-1-n',
67 67 callback : function () {
68 68 IPython.notebook.move_cell_up();
69 69 }
70 70 },
71 71 {
72 72 id : 'move_down_b',
73 73 label : 'Move Cell Down',
74 74 icon : 'ui-icon-arrowthick-1-s',
75 75 callback : function () {
76 76 IPython.notebook.move_cell_down();
77 77 }
78 78 }
79 79 ],'move_up_down');
80 80
81 81 this.add_buttons_group([
82 82 {
83 83 id : 'insert_above_b',
84 84 label : 'Insert Cell Above',
85 85 icon : 'ui-icon-arrowthickstop-1-n',
86 86 callback : function () {
87 87 IPython.notebook.insert_cell_above('code');
88 88 }
89 89 },
90 90 {
91 91 id : 'insert_below_b',
92 92 label : 'Insert Cell Below',
93 93 icon : 'ui-icon-arrowthickstop-1-s',
94 94 callback : function () {
95 95 IPython.notebook.insert_cell_below('code');
96 96 }
97 97 }
98 98 ],'insert_above_below');
99 99
100 100 this.add_buttons_group([
101 101 {
102 102 id : 'run_b',
103 103 label : 'Run Cell',
104 104 icon : 'ui-icon-play',
105 105 callback : function () {
106 106 IPython.notebook.execute_selected_cell();
107 107 }
108 108 },
109 109 {
110 110 id : 'interrupt_b',
111 111 label : 'Interrupt',
112 112 icon : 'ui-icon-stop',
113 113 callback : function () {
114 114 IPython.notebook.kernel.interrupt();
115 115 }
116 116 }
117 117 ],'run_int');
118 118
119 119
120 120 };
121 121
122 122 MainToolBar.prototype.add_drop_down_list = function () {
123 123 var select = $(this.selector)
124 124 .append($('<select/>')
125 125 .attr('id','cell_type')
126 126 .addClass('ui-widget ui-widget-content')
127 127 .append($('<option/>').attr('value','code').text('Code'))
128 128 .append($('<option/>').attr('value','markdown').text('Markdown'))
129 129 .append($('<option/>').attr('value','raw').text('Raw Text'))
130 130 .append($('<option/>').attr('value','heading1').text('Heading 1'))
131 131 .append($('<option/>').attr('value','heading2').text('Heading 2'))
132 132 .append($('<option/>').attr('value','heading3').text('Heading 3'))
133 133 .append($('<option/>').attr('value','heading4').text('Heading 4'))
134 134 .append($('<option/>').attr('value','heading5').text('Heading 5'))
135 135 .append($('<option/>').attr('value','heading6').text('Heading 6'))
136 136 .append($('<option/>').attr('value','heading7').text('Heading 7'))
137 137 .append($('<option/>').attr('value','heading8').text('Heading 8'))
138 138 );
139 139 };
140 140
141 141 MainToolBar.prototype.bind_events = function () {
142 142 var that = this;
143 143
144 144 this.element.find('#cell_type').change(function () {
145 145 var cell_type = $(this).val();
146 146 if (cell_type === 'code') {
147 147 IPython.notebook.to_code();
148 148 } else if (cell_type === 'markdown') {
149 149 IPython.notebook.to_markdown();
150 150 } else if (cell_type === 'raw') {
151 151 IPython.notebook.to_raw();
152 152 } else if (cell_type === 'heading1') {
153 153 IPython.notebook.to_heading(undefined, 1);
154 154 } else if (cell_type === 'heading2') {
155 155 IPython.notebook.to_heading(undefined, 2);
156 156 } else if (cell_type === 'heading3') {
157 157 IPython.notebook.to_heading(undefined, 3);
158 158 } else if (cell_type === 'heading4') {
159 159 IPython.notebook.to_heading(undefined, 4);
160 160 } else if (cell_type === 'heading5') {
161 161 IPython.notebook.to_heading(undefined, 5);
162 162 } else if (cell_type === 'heading6') {
163 163 IPython.notebook.to_heading(undefined, 6);
164 164 }
165 165 });
166 166 $([IPython.events]).on('selected_cell_type_changed.Notebook', function (event, data) {
167 167 if (data.cell_type === 'heading') {
168 168 that.element.find('#cell_type').val(data.cell_type+data.level);
169 169 } else {
170 170 that.element.find('#cell_type').val(data.cell_type);
171 171 }
172 172 });
173 173 };
174 174
175 175 IPython.MainToolBar = MainToolBar;
176 176
177 177 return IPython;
178 178
179 179 }(IPython));
@@ -1,243 +1,241 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2012 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // MathJax utility functions
10 10 //============================================================================
11 11
12 12 IPython.namespace('IPython.mathjaxutils');
13 13
14 14 IPython.mathjaxutils = (function (IPython) {
15 15
16 16 var init = function () {
17 if (window.MathJax) {
17 if (window.MathJax) {
18 18 // MathJax loaded
19 19 MathJax.Hub.Config({
20 TeX: { equationNumbers: { autoNumber: "AMS", useLabelIds: true } },
21 20 tex2jax: {
22 21 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
23 22 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
24 23 processEnvironments: true
25 24 },
26 25 displayAlign: 'left', // Change this to 'center' to center equations.
27 26 "HTML-CSS": {
28 27 styles: {'.MathJax_Display': {"margin": 0}}
29 28 }
30 29 });
30 MathJax.Hub.Configured();
31 31 } else if (window.mathjax_url != "") {
32 32 // Don't have MathJax, but should. Show dialog.
33 33 var dialog = $('<div></div>')
34 34 .append(
35 35 $("<p></p>").addClass('dialog').html(
36 36 "Math/LaTeX rendering will be disabled."
37 37 )
38 38 ).append(
39 39 $("<p></p>").addClass('dialog').html(
40 40 "If you have administrative access to the notebook server and" +
41 41 " a working internet connection, you can install a local copy" +
42 42 " of MathJax for offline use with the following command on the server" +
43 43 " at a Python or IPython prompt:"
44 44 )
45 45 ).append(
46 46 $("<pre></pre>").addClass('dialog').html(
47 47 ">>> from IPython.external import mathjax; mathjax.install_mathjax()"
48 48 )
49 49 ).append(
50 50 $("<p></p>").addClass('dialog').html(
51 51 "This will try to install MathJax into the IPython source directory."
52 52 )
53 53 ).append(
54 54 $("<p></p>").addClass('dialog').html(
55 55 "If IPython is installed to a location that requires" +
56 56 " administrative privileges to write, you will need to make this call as" +
57 57 " an administrator, via 'sudo'."
58 58 )
59 59 ).append(
60 60 $("<p></p>").addClass('dialog').html(
61 61 "When you start the notebook server, you can instruct it to disable MathJax support altogether:"
62 62 )
63 63 ).append(
64 64 $("<pre></pre>").addClass('dialog').html(
65 65 "$ ipython notebook --no-mathjax"
66 66 )
67 67 ).append(
68 68 $("<p></p>").addClass('dialog').html(
69 69 "which will prevent this dialog from appearing."
70 70 )
71 71 ).dialog({
72 72 title: "Failed to retrieve MathJax from '" + window.mathjax_url + "'",
73 73 width: "70%",
74 74 modal: true,
75 75 })
76 76 } else {
77 77 // No MathJax, but none expected. No dialog.
78 78 };
79 79 };
80 80
81 81 // Some magic for deferring mathematical expressions to MathJax
82 82 // by hiding them from the Markdown parser.
83 83 // Some of the code here is adapted with permission from Davide Cervone
84 84 // under the terms of the Apache2 license governing the MathJax project.
85 85 // Other minor modifications are also due to StackExchange and are used with
86 86 // permission.
87 87
88 88 var inline = "$"; // the inline math delimiter
89 89 var blocks, start, end, last, braces; // used in searching for math
90 90 var math; // stores math until pagedown (Markdown parser) is done
91 var HUB = MathJax.Hub;
92 91
93 92 // MATHSPLIT contains the pattern for math delimiters and special symbols
94 93 // needed for searching for math in the text input.
95 94 var MATHSPLIT = /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[\\{}$]|[{}]|(?:\n\s*)+|@@\d+@@)/i;
96 95
97 96 // The math is in blocks i through j, so
98 97 // collect it into one block and clear the others.
99 98 // Replace &, <, and > by named entities.
100 99 // For IE, put <br> at the ends of comments since IE removes \n.
101 100 // Clear the current math positions and store the index of the
102 101 // math, then push the math string onto the storage array.
103 102 // The preProcess function is called on all blocks if it has been passed in
104 103 var process_math = function (i, j, pre_process) {
104 var hub = MathJax.Hub;
105 105 var block = blocks.slice(i, j + 1).join("").replace(/&/g, "&amp;") // use HTML entity for &
106 106 .replace(/</g, "&lt;") // use HTML entity for <
107 107 .replace(/>/g, "&gt;") // use HTML entity for >
108 108 ;
109 if (HUB.Browser.isMSIE) {
109 if (hub.Browser.isMSIE) {
110 110 block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n")
111 111 }
112 112 while (j > i) {
113 113 blocks[j] = "";
114 114 j--;
115 115 }
116 116 blocks[i] = "@@" + math.length + "@@"; // replace the current block text with a unique tag to find later
117 117 if (pre_process)
118 118 block = pre_process(block);
119 119 math.push(block);
120 120 start = end = last = null;
121 121 }
122 122
123 123 // Break up the text into its component parts and search
124 124 // through them for math delimiters, braces, linebreaks, etc.
125 125 // Math delimiters must match and braces must balance.
126 126 // Don't allow math to pass through a double linebreak
127 127 // (which will be a paragraph).
128 128 //
129 129 var remove_math = function (text) {
130 if (!window.MathJax) {
131 return text;
132 }
133
130 134 start = end = last = null; // for tracking math delimiters
131 135 math = []; // stores math strings for later
132 136
133 137 // Except for extreme edge cases, this should catch precisely those pieces of the markdown
134 138 // source that will later be turned into code spans. While MathJax will not TeXify code spans,
135 139 // we still have to consider them at this point; the following issue has happened several times:
136 140 //
137 141 // `$foo` and `$bar` are varibales. --> <code>$foo ` and `$bar</code> are variables.
138 142
139 143 var hasCodeSpans = /`/.test(text),
140 144 de_tilde;
141 145 if (hasCodeSpans) {
142 146 text = text.replace(/~/g, "~T").replace(/(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm, function (wholematch) {
143 147 return wholematch.replace(/\$/g, "~D");
144 148 });
145 149 de_tilde = function (text) { return text.replace(/~([TD])/g, function (wholematch, character) { return { T: "~", D: "$" }[character]; }) };
146 150 } else {
147 151 de_tilde = function (text) { return text; };
148 152 }
149 153
150 154 blocks = IPython.utils.regex_split(text.replace(/\r\n?/g, "\n"),MATHSPLIT);
151 155
152 156 for (var i = 1, m = blocks.length; i < m; i += 2) {
153 157 var block = blocks[i];
154 158 if (block.charAt(0) === "@") {
155 159 //
156 160 // Things that look like our math markers will get
157 161 // stored and then retrieved along with the math.
158 162 //
159 163 blocks[i] = "@@" + math.length + "@@";
160 164 math.push(block);
161 165 }
162 166 else if (start) {
163 167 //
164 168 // If we are in math, look for the end delimiter,
165 169 // but don't go past double line breaks, and
166 170 // and balance braces within the math.
167 171 //
168 172 if (block === end) {
169 173 if (braces) {
170 174 last = i
171 175 }
172 176 else {
173 177 process_math(start, i, de_tilde)
174 178 }
175 179 }
176 180 else if (block.match(/\n.*\n/)) {
177 181 if (last) {
178 182 i = last;
179 183 process_math(start, i, de_tilde)
180 184 }
181 185 start = end = last = null;
182 186 braces = 0;
183 187 }
184 188 else if (block === "{") {
185 189 braces++
186 190 }
187 191 else if (block === "}" && braces) {
188 192 braces--
189 193 }
190 194 }
191 195 else {
192 196 //
193 197 // Look for math start delimiters and when
194 198 // found, set up the end delimiter.
195 199 //
196 200 if (block === inline || block === "$$") {
197 201 start = i;
198 202 end = block;
199 203 braces = 0;
200 204 }
201 205 else if (block.substr(1, 5) === "begin") {
202 206 start = i;
203 207 end = "\\end" + block.substr(6);
204 208 braces = 0;
205 209 }
206 210 }
207 211 }
208 212 if (last) {
209 213 process_math(start, last, de_tilde)
210 214 }
211 215 return de_tilde(blocks.join(""));
212 216 }
213 217
214 218 //
215 219 // Put back the math strings that were saved,
216 220 // and clear the math array (no need to keep it around).
217 221 //
218 222 var replace_math = function (text) {
223 if (!window.MathJax) {
224 return text;
225 }
226
219 227 text = text.replace(/@@(\d+)@@/g, function (match, n) {
220 228 return math[n]
221 229 });
222 230 math = null;
223 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 234 return {
236 235 init : init,
237 236 process_math : process_math,
238 237 remove_math : remove_math,
239 replace_math : replace_math,
240 queue_render : queue_render
238 replace_math : replace_math
241 239 };
242 240
243 241 }(IPython)); No newline at end of file
@@ -1,192 +1,198 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // MenuBar
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var MenuBar = function (selector) {
15 15 this.selector = selector;
16 16 if (this.selector !== undefined) {
17 17 this.element = $(selector);
18 18 this.style();
19 19 this.bind_events();
20 20 }
21 21 };
22 22
23 23
24 24 MenuBar.prototype.style = function () {
25 25 this.element.addClass('border-box-sizing');
26 26 $('ul#menus').menubar({
27 27 select : function (event, ui) {
28 28 // The selected cell loses focus when the menu is entered, so we
29 29 // re-select it upon selection.
30 30 var i = IPython.notebook.get_selected_index();
31 31 IPython.notebook.select(i);
32 32 }
33 33 });
34 34 };
35 35
36 36
37 37 MenuBar.prototype.bind_events = function () {
38 38 // File
39 39 this.element.find('#new_notebook').click(function () {
40 40 window.open($('body').data('baseProjectUrl')+'new');
41 41 });
42 42 this.element.find('#open_notebook').click(function () {
43 43 window.open($('body').data('baseProjectUrl'));
44 44 });
45 45 this.element.find('#rename_notebook').click(function () {
46 46 IPython.save_widget.rename_notebook();
47 47 });
48 48 this.element.find('#copy_notebook').click(function () {
49 49 var notebook_id = IPython.notebook.get_notebook_id();
50 50 var url = $('body').data('baseProjectUrl') + notebook_id + '/copy';
51 51 window.open(url,'_blank');
52 52 return false;
53 53 });
54 54 this.element.find('#save_notebook').click(function () {
55 55 IPython.notebook.save_notebook();
56 56 });
57 57 this.element.find('#download_ipynb').click(function () {
58 58 var notebook_id = IPython.notebook.get_notebook_id();
59 59 var url = $('body').data('baseProjectUrl') + 'notebooks/' +
60 60 notebook_id + '?format=json';
61 61 window.open(url,'_newtab');
62 62 });
63 63 this.element.find('#download_py').click(function () {
64 64 var notebook_id = IPython.notebook.get_notebook_id();
65 65 var url = $('body').data('baseProjectUrl') + 'notebooks/' +
66 66 notebook_id + '?format=py';
67 67 window.open(url,'_newtab');
68 68 });
69 69 this.element.find('button#print_notebook').click(function () {
70 70 IPython.print_widget.print_notebook();
71 71 });
72 72 this.element.find('#kill_and_exit').click(function () {
73 73 IPython.notebook.kernel.kill();
74 74 setTimeout(function(){window.close();}, 200);
75 75 });
76 76 // Edit
77 77 this.element.find('#cut_cell').click(function () {
78 78 IPython.notebook.cut_cell();
79 79 });
80 80 this.element.find('#copy_cell').click(function () {
81 81 IPython.notebook.copy_cell();
82 82 });
83 83 this.element.find('#delete_cell').click(function () {
84 84 IPython.notebook.delete_cell();
85 85 });
86 86 this.element.find('#split_cell').click(function () {
87 87 IPython.notebook.split_cell();
88 88 });
89 89 this.element.find('#merge_cell_above').click(function () {
90 90 IPython.notebook.merge_cell_above();
91 91 });
92 92 this.element.find('#merge_cell_below').click(function () {
93 93 IPython.notebook.merge_cell_below();
94 94 });
95 95 this.element.find('#move_cell_up').click(function () {
96 96 IPython.notebook.move_cell_up();
97 97 });
98 98 this.element.find('#move_cell_down').click(function () {
99 99 IPython.notebook.move_cell_down();
100 100 });
101 101 this.element.find('#select_previous').click(function () {
102 102 IPython.notebook.select_prev();
103 103 });
104 104 this.element.find('#select_next').click(function () {
105 105 IPython.notebook.select_next();
106 106 });
107 107 // View
108 108 this.element.find('#toggle_header').click(function () {
109 109 $('div#header').toggle();
110 110 IPython.layout_manager.do_resize();
111 111 });
112 112 this.element.find('#toggle_toolbar').click(function () {
113 113 IPython.toolbar.toggle();
114 114 });
115 115 // Insert
116 116 this.element.find('#insert_cell_above').click(function () {
117 117 IPython.notebook.insert_cell_above('code');
118 118 });
119 119 this.element.find('#insert_cell_below').click(function () {
120 120 IPython.notebook.insert_cell_below('code');
121 121 });
122 122 // Cell
123 123 this.element.find('#run_cell').click(function () {
124 124 IPython.notebook.execute_selected_cell();
125 125 });
126 126 this.element.find('#run_cell_in_place').click(function () {
127 127 IPython.notebook.execute_selected_cell({terminal:true});
128 128 });
129 129 this.element.find('#run_all_cells').click(function () {
130 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 138 this.element.find('#to_code').click(function () {
133 139 IPython.notebook.to_code();
134 140 });
135 141 this.element.find('#to_markdown').click(function () {
136 142 IPython.notebook.to_markdown();
137 143 });
138 144 this.element.find('#to_raw').click(function () {
139 145 IPython.notebook.to_raw();
140 146 });
141 147 this.element.find('#to_heading1').click(function () {
142 148 IPython.notebook.to_heading(undefined, 1);
143 149 });
144 150 this.element.find('#to_heading2').click(function () {
145 151 IPython.notebook.to_heading(undefined, 2);
146 152 });
147 153 this.element.find('#to_heading3').click(function () {
148 154 IPython.notebook.to_heading(undefined, 3);
149 155 });
150 156 this.element.find('#to_heading4').click(function () {
151 157 IPython.notebook.to_heading(undefined, 4);
152 158 });
153 159 this.element.find('#to_heading5').click(function () {
154 160 IPython.notebook.to_heading(undefined, 5);
155 161 });
156 162 this.element.find('#to_heading6').click(function () {
157 163 IPython.notebook.to_heading(undefined, 6);
158 164 });
159 165 this.element.find('#toggle_output').click(function () {
160 166 IPython.notebook.toggle_output();
161 167 });
162 168 this.element.find('#collapse_all_output').click(function () {
163 169 IPython.notebook.collapse_all_output();
164 170 });
165 171 this.element.find('#scroll_all_output').click(function () {
166 172 IPython.notebook.scroll_all_output();
167 173 });
168 174 this.element.find('#expand_all_output').click(function () {
169 175 IPython.notebook.expand_all_output();
170 176 });
171 177 this.element.find('#clear_all_output').click(function () {
172 178 IPython.notebook.clear_all_output();
173 179 });
174 180 // Kernel
175 181 this.element.find('#int_kernel').click(function () {
176 182 IPython.notebook.kernel.interrupt();
177 183 });
178 184 this.element.find('#restart_kernel').click(function () {
179 185 IPython.notebook.restart_kernel();
180 186 });
181 187 // Help
182 188 this.element.find('#keyboard_shortcuts').click(function () {
183 189 IPython.quick_help.show_keyboard_shortcuts();
184 190 });
185 191 };
186 192
187 193
188 194 IPython.MenuBar = MenuBar;
189 195
190 196 return IPython;
191 197
192 198 }(IPython));
@@ -1,1310 +1,1371 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Notebook
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var utils = IPython.utils;
15 15 var key = IPython.utils.keycodes;
16 16
17 17 var Notebook = function (selector) {
18 18 this.read_only = IPython.read_only;
19 19 this.element = $(selector);
20 20 this.element.scroll();
21 21 this.element.data("notebook", this);
22 22 this.next_prompt_number = 1;
23 23 this.kernel = null;
24 24 this.clipboard = null;
25 this.undelete_backup = null;
26 this.undelete_index = null;
27 this.undelete_below = false;
25 28 this.paste_enabled = false;
26 29 this.dirty = false;
27 30 this.metadata = {};
28 31 // single worksheet for now
29 32 this.worksheet_metadata = {};
30 33 this.control_key_active = false;
31 34 this.notebook_id = null;
32 35 this.notebook_name = null;
33 36 this.notebook_name_blacklist_re = /[\/\\:]/;
34 37 this.nbformat = 3 // Increment this when changing the nbformat
35 38 this.nbformat_minor = 0 // Increment this when changing the nbformat
36 39 this.style();
37 40 this.create_elements();
38 41 this.bind_events();
39 42 };
40 43
41 44
42 45 Notebook.prototype.style = function () {
43 46 $('div#notebook').addClass('border-box-sizing');
44 47 };
45 48
46 49
47 50 Notebook.prototype.create_elements = function () {
48 51 // We add this end_space div to the end of the notebook div to:
49 52 // i) provide a margin between the last cell and the end of the notebook
50 53 // ii) to prevent the div from scrolling up when the last cell is being
51 54 // edited, but is too low on the page, which browsers will do automatically.
52 55 var that = this;
53 56 var end_space = $('<div/>').addClass('end_space').height("30%");
54 57 end_space.dblclick(function (e) {
55 58 if (that.read_only) return;
56 59 var ncells = that.ncells();
57 60 that.insert_cell_below('code',ncells-1);
58 61 });
59 62 this.element.append(end_space);
60 63 $('div#notebook').addClass('border-box-sizing');
61 64 };
62 65
63 66
64 67 Notebook.prototype.bind_events = function () {
65 68 var that = this;
66 69
67 70 $([IPython.events]).on('set_next_input.Notebook', function (event, data) {
68 71 var index = that.find_cell_index(data.cell);
69 72 var new_cell = that.insert_cell_below('code',index);
70 73 new_cell.set_text(data.text);
71 74 that.dirty = true;
72 75 });
73 76
74 77 $([IPython.events]).on('set_dirty.Notebook', function (event, data) {
75 78 that.dirty = data.value;
76 79 });
77 80
78 81 $([IPython.events]).on('select.Cell', function (event, data) {
79 82 var index = that.find_cell_index(data.cell);
80 83 that.select(index);
81 84 });
82 85
83 86
84 87 $(document).keydown(function (event) {
85 88 // console.log(event);
86 89 if (that.read_only) return true;
87 90
88 91 // Save (CTRL+S) or (AppleKey+S)
89 92 //metaKey = applekey on mac
90 93 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
91 94 that.save_notebook();
92 95 event.preventDefault();
93 96 return false;
94 97 } else if (event.which === key.ESC) {
95 98 // Intercept escape at highest level to avoid closing
96 99 // websocket connection with firefox
97 100 event.preventDefault();
98 101 } else if (event.which === key.SHIFT) {
99 102 // ignore shift keydown
100 103 return true;
101 104 }
102 105 if (event.which === key.UPARROW && !event.shiftKey) {
103 106 var cell = that.get_selected_cell();
104 107 if (cell.at_top()) {
105 108 event.preventDefault();
106 109 that.select_prev();
107 110 };
108 111 } else if (event.which === key.DOWNARROW && !event.shiftKey) {
109 112 var cell = that.get_selected_cell();
110 113 if (cell.at_bottom()) {
111 114 event.preventDefault();
112 115 that.select_next();
113 116 };
114 117 } else if (event.which === key.ENTER && event.shiftKey) {
115 118 that.execute_selected_cell();
116 119 return false;
117 120 } else if (event.which === key.ENTER && event.altKey) {
118 121 // Execute code cell, and insert new in place
119 122 that.execute_selected_cell();
120 123 // Only insert a new cell, if we ended up in an already populated cell
121 124 if (/\S/.test(that.get_selected_cell().get_text()) == true) {
122 125 that.insert_cell_above('code');
123 126 }
124 127 return false;
125 128 } else if (event.which === key.ENTER && event.ctrlKey) {
126 129 that.execute_selected_cell({terminal:true});
127 130 return false;
128 131 } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) {
129 132 that.control_key_active = true;
130 133 return false;
131 134 } else if (event.which === 88 && that.control_key_active) {
132 135 // Cut selected cell = x
133 136 that.cut_cell();
134 137 that.control_key_active = false;
135 138 return false;
136 139 } else if (event.which === 67 && that.control_key_active) {
137 140 // Copy selected cell = c
138 141 that.copy_cell();
139 142 that.control_key_active = false;
140 143 return false;
141 144 } else if (event.which === 86 && that.control_key_active) {
142 // Paste selected cell = v
143 that.paste_cell();
145 // Paste below selected cell = v
146 that.paste_cell_below();
144 147 that.control_key_active = false;
145 148 return false;
146 149 } else if (event.which === 68 && that.control_key_active) {
147 150 // Delete selected cell = d
148 151 that.delete_cell();
149 152 that.control_key_active = false;
150 153 return false;
151 154 } else if (event.which === 65 && that.control_key_active) {
152 155 // Insert code cell above selected = a
153 156 that.insert_cell_above('code');
154 157 that.control_key_active = false;
155 158 return false;
156 159 } else if (event.which === 66 && that.control_key_active) {
157 160 // Insert code cell below selected = b
158 161 that.insert_cell_below('code');
159 162 that.control_key_active = false;
160 163 return false;
161 164 } else if (event.which === 89 && that.control_key_active) {
162 165 // To code = y
163 166 that.to_code();
164 167 that.control_key_active = false;
165 168 return false;
166 169 } else if (event.which === 77 && that.control_key_active) {
167 170 // To markdown = m
168 171 that.to_markdown();
169 172 that.control_key_active = false;
170 173 return false;
171 174 } else if (event.which === 84 && that.control_key_active) {
172 175 // To Raw = t
173 176 that.to_raw();
174 177 that.control_key_active = false;
175 178 return false;
176 179 } else if (event.which === 49 && that.control_key_active) {
177 180 // To Heading 1 = 1
178 181 that.to_heading(undefined, 1);
179 182 that.control_key_active = false;
180 183 return false;
181 184 } else if (event.which === 50 && that.control_key_active) {
182 185 // To Heading 2 = 2
183 186 that.to_heading(undefined, 2);
184 187 that.control_key_active = false;
185 188 return false;
186 189 } else if (event.which === 51 && that.control_key_active) {
187 190 // To Heading 3 = 3
188 191 that.to_heading(undefined, 3);
189 192 that.control_key_active = false;
190 193 return false;
191 194 } else if (event.which === 52 && that.control_key_active) {
192 195 // To Heading 4 = 4
193 196 that.to_heading(undefined, 4);
194 197 that.control_key_active = false;
195 198 return false;
196 199 } else if (event.which === 53 && that.control_key_active) {
197 200 // To Heading 5 = 5
198 201 that.to_heading(undefined, 5);
199 202 that.control_key_active = false;
200 203 return false;
201 204 } else if (event.which === 54 && that.control_key_active) {
202 205 // To Heading 6 = 6
203 206 that.to_heading(undefined, 6);
204 207 that.control_key_active = false;
205 208 return false;
206 209 } else if (event.which === 79 && that.control_key_active) {
207 210 // Toggle output = o
208 211 if (event.shiftKey){
209 212 that.toggle_output_scroll();
210 213 } else {
211 214 that.toggle_output();
212 215 }
213 216 that.control_key_active = false;
214 217 return false;
215 218 } else if (event.which === 83 && that.control_key_active) {
216 219 // Save notebook = s
217 220 that.save_notebook();
218 221 that.control_key_active = false;
219 222 return false;
220 223 } else if (event.which === 74 && that.control_key_active) {
221 224 // Move cell down = j
222 225 that.move_cell_down();
223 226 that.control_key_active = false;
224 227 return false;
225 228 } else if (event.which === 75 && that.control_key_active) {
226 229 // Move cell up = k
227 230 that.move_cell_up();
228 231 that.control_key_active = false;
229 232 return false;
230 233 } else if (event.which === 80 && that.control_key_active) {
231 234 // Select previous = p
232 235 that.select_prev();
233 236 that.control_key_active = false;
234 237 return false;
235 238 } else if (event.which === 78 && that.control_key_active) {
236 239 // Select next = n
237 240 that.select_next();
238 241 that.control_key_active = false;
239 242 return false;
240 243 } else if (event.which === 76 && that.control_key_active) {
241 244 // Toggle line numbers = l
242 245 that.cell_toggle_line_numbers();
243 246 that.control_key_active = false;
244 247 return false;
245 248 } else if (event.which === 73 && that.control_key_active) {
246 249 // Interrupt kernel = i
247 250 that.kernel.interrupt();
248 251 that.control_key_active = false;
249 252 return false;
250 253 } else if (event.which === 190 && that.control_key_active) {
251 254 // Restart kernel = . # matches qt console
252 255 that.restart_kernel();
253 256 that.control_key_active = false;
254 257 return false;
255 258 } else if (event.which === 72 && that.control_key_active) {
256 259 // Show keyboard shortcuts = h
257 260 IPython.quick_help.show_keyboard_shortcuts();
258 261 that.control_key_active = false;
259 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 268 } else if (that.control_key_active) {
261 269 that.control_key_active = false;
262 270 return true;
263 271 };
264 272 return true;
265 273 });
266 274
267 275 var collapse_time = function(time){
268 276 var app_height = $('div#main_app').height(); // content height
269 277 var splitter_height = $('div#pager_splitter').outerHeight(true);
270 278 var new_height = app_height - splitter_height;
271 279 that.element.animate({height : new_height + 'px'}, time);
272 280 }
273 281
274 282 this.element.bind('collapse_pager', function (event,extrap) {
275 283 time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
276 284 collapse_time(time);
277 285 });
278 286
279 287 var expand_time = function(time) {
280 288 var app_height = $('div#main_app').height(); // content height
281 289 var splitter_height = $('div#pager_splitter').outerHeight(true);
282 290 var pager_height = $('div#pager').outerHeight(true);
283 291 var new_height = app_height - pager_height - splitter_height;
284 292 that.element.animate({height : new_height + 'px'}, time);
285 293 }
286 294
287 295 this.element.bind('expand_pager', function (event, extrap) {
288 296 time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
289 297 expand_time(time);
290 298 });
291 299
292 300 $(window).bind('beforeunload', function () {
293 301 // TODO: Make killing the kernel configurable.
294 302 var kill_kernel = false;
295 303 if (kill_kernel) {
296 304 that.kernel.kill();
297 305 }
298 306 if (that.dirty && ! that.read_only) {
299 307 return "You have unsaved changes that will be lost if you leave this page.";
300 308 };
301 309 // Null is the *only* return value that will make the browser not
302 310 // pop up the "don't leave" dialog.
303 311 return null;
304 312 });
305 313 };
306 314
307 315 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
308 316 var cells = this.get_cells();
309 317 var time = time || 0;
310 318 cell_number = Math.min(cells.length-1,cell_number);
311 319 cell_number = Math.max(0 ,cell_number);
312 320 scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
313 321 this.element.animate({scrollTop:scroll_value}, time);
314 322 return scroll_value;
315 323 };
316 324
317 325
318 326 Notebook.prototype.scroll_to_bottom = function () {
319 327 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
320 328 };
321 329
322 330
323 331 Notebook.prototype.scroll_to_top = function () {
324 332 this.element.animate({scrollTop:0}, 0);
325 333 };
326 334
327 335
328 336 // Cell indexing, retrieval, etc.
329 337
330 338 Notebook.prototype.get_cell_elements = function () {
331 339 return this.element.children("div.cell");
332 340 };
333 341
334 342
335 343 Notebook.prototype.get_cell_element = function (index) {
336 344 var result = null;
337 345 var e = this.get_cell_elements().eq(index);
338 346 if (e.length !== 0) {
339 347 result = e;
340 348 }
341 349 return result;
342 350 };
343 351
344 352
345 353 Notebook.prototype.ncells = function (cell) {
346 354 return this.get_cell_elements().length;
347 355 };
348 356
349 357
350 358 // TODO: we are often calling cells as cells()[i], which we should optimize
351 359 // to cells(i) or a new method.
352 360 Notebook.prototype.get_cells = function () {
353 361 return this.get_cell_elements().toArray().map(function (e) {
354 362 return $(e).data("cell");
355 363 });
356 364 };
357 365
358 366
359 367 Notebook.prototype.get_cell = function (index) {
360 368 var result = null;
361 369 var ce = this.get_cell_element(index);
362 370 if (ce !== null) {
363 371 result = ce.data('cell');
364 372 }
365 373 return result;
366 374 }
367 375
368 376
369 377 Notebook.prototype.get_next_cell = function (cell) {
370 378 var result = null;
371 379 var index = this.find_cell_index(cell);
372 380 if (index !== null && index < this.ncells()) {
373 381 result = this.get_cell(index+1);
374 382 }
375 383 return result;
376 384 }
377 385
378 386
379 387 Notebook.prototype.get_prev_cell = function (cell) {
380 388 var result = null;
381 389 var index = this.find_cell_index(cell);
382 390 if (index !== null && index > 1) {
383 391 result = this.get_cell(index-1);
384 392 }
385 393 return result;
386 394 }
387 395
388 396 Notebook.prototype.find_cell_index = function (cell) {
389 397 var result = null;
390 398 this.get_cell_elements().filter(function (index) {
391 399 if ($(this).data("cell") === cell) {
392 400 result = index;
393 401 };
394 402 });
395 403 return result;
396 404 };
397 405
398 406
399 407 Notebook.prototype.index_or_selected = function (index) {
400 408 var i;
401 409 if (index === undefined || index === null) {
402 410 i = this.get_selected_index();
403 411 if (i === null) {
404 412 i = 0;
405 413 }
406 414 } else {
407 415 i = index;
408 416 }
409 417 return i;
410 418 };
411 419
412 420
413 421 Notebook.prototype.get_selected_cell = function () {
414 422 var index = this.get_selected_index();
415 423 return this.get_cell(index);
416 424 };
417 425
418 426
419 427 Notebook.prototype.is_valid_cell_index = function (index) {
420 428 if (index !== null && index >= 0 && index < this.ncells()) {
421 429 return true;
422 430 } else {
423 431 return false;
424 432 };
425 433 }
426 434
427 435 Notebook.prototype.get_selected_index = function () {
428 436 var result = null;
429 437 this.get_cell_elements().filter(function (index) {
430 438 if ($(this).data("cell").selected === true) {
431 439 result = index;
432 440 };
433 441 });
434 442 return result;
435 443 };
436 444
437 445
438 446 // Cell selection.
439 447
440 448 Notebook.prototype.select = function (index) {
441 449 if (index !== undefined && index >= 0 && index < this.ncells()) {
442 450 sindex = this.get_selected_index()
443 451 if (sindex !== null && index !== sindex) {
444 452 this.get_cell(sindex).unselect();
445 453 };
446 454 var cell = this.get_cell(index)
447 455 cell.select();
448 456 if (cell.cell_type === 'heading') {
449 457 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
450 458 {'cell_type':cell.cell_type,level:cell.level}
451 459 );
452 460 } else {
453 461 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
454 462 {'cell_type':cell.cell_type}
455 463 );
456 464 };
457 465 };
458 466 return this;
459 467 };
460 468
461 469
462 470 Notebook.prototype.select_next = function () {
463 471 var index = this.get_selected_index();
464 472 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
465 473 this.select(index+1);
466 474 };
467 475 return this;
468 476 };
469 477
470 478
471 479 Notebook.prototype.select_prev = function () {
472 480 var index = this.get_selected_index();
473 481 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
474 482 this.select(index-1);
475 483 };
476 484 return this;
477 485 };
478 486
479 487
480 488 // Cell movement
481 489
482 490 Notebook.prototype.move_cell_up = function (index) {
483 491 var i = this.index_or_selected();
484 492 if (i !== null && i < this.ncells() && i > 0) {
485 493 var pivot = this.get_cell_element(i-1);
486 494 var tomove = this.get_cell_element(i);
487 495 if (pivot !== null && tomove !== null) {
488 496 tomove.detach();
489 497 pivot.before(tomove);
490 498 this.select(i-1);
491 499 };
492 500 };
493 501 this.dirty = true;
494 502 return this;
495 503 };
496 504
497 505
498 506 Notebook.prototype.move_cell_down = function (index) {
499 507 var i = this.index_or_selected();
500 508 if (i !== null && i < (this.ncells()-1) && i >= 0) {
501 509 var pivot = this.get_cell_element(i+1);
502 510 var tomove = this.get_cell_element(i);
503 511 if (pivot !== null && tomove !== null) {
504 512 tomove.detach();
505 513 pivot.after(tomove);
506 514 this.select(i+1);
507 515 };
508 516 };
509 517 this.dirty = true;
510 518 return this;
511 519 };
512 520
513 521
514 522 Notebook.prototype.sort_cells = function () {
515 523 // This is not working right now. Calling this will actually crash
516 524 // the browser. I think there is an infinite loop in here...
517 525 var ncells = this.ncells();
518 526 var sindex = this.get_selected_index();
519 527 var swapped;
520 528 do {
521 529 swapped = false;
522 530 for (var i=1; i<ncells; i++) {
523 531 current = this.get_cell(i);
524 532 previous = this.get_cell(i-1);
525 533 if (previous.input_prompt_number > current.input_prompt_number) {
526 534 this.move_cell_up(i);
527 535 swapped = true;
528 536 };
529 537 };
530 538 } while (swapped);
531 539 this.select(sindex);
532 540 return this;
533 541 };
534 542
535 543 // Insertion, deletion.
536 544
537 545 Notebook.prototype.delete_cell = function (index) {
538 546 var i = this.index_or_selected(index);
547 var cell = this.get_selected_cell();
548 this.undelete_backup = cell.toJSON();
539 549 if (this.is_valid_cell_index(i)) {
540 550 var ce = this.get_cell_element(i);
541 551 ce.remove();
542 552 if (i === (this.ncells())) {
543 553 this.select(i-1);
554 this.undelete_index = i - 1;
555 this.undelete_below = true;
544 556 } else {
545 557 this.select(i);
558 this.undelete_index = i;
559 this.undelete_below = false;
546 560 };
547 561 this.dirty = true;
548 562 };
549 563 return this;
550 564 };
551 565
552 566
553 567 Notebook.prototype.insert_cell_at_bottom = function (type){
554 568 var len = this.ncells();
555 569 return this.insert_cell_below(type,len-1);
556 570 }
557 571
558 572 Notebook.prototype.insert_cell_below = function (type, index) {
559 573 // type = ('code','html','markdown')
560 574 // index = cell index or undefined to insert below selected
561 575 index = this.index_or_selected(index);
562 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 582 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
564 583 if (type === 'code') {
565 584 cell = new IPython.CodeCell(this.kernel);
566 585 cell.set_input_prompt();
567 586 } else if (type === 'markdown') {
568 587 cell = new IPython.MarkdownCell();
569 588 } else if (type === 'html') {
570 589 cell = new IPython.HTMLCell();
571 590 } else if (type === 'raw') {
572 591 cell = new IPython.RawCell();
573 592 } else if (type === 'heading') {
574 593 cell = new IPython.HeadingCell();
575 594 };
576 595 if (cell !== null) {
577 596 if (this.ncells() === 0) {
578 597 this.element.find('div.end_space').before(cell.element);
579 598 } else if (this.is_valid_cell_index(index)) {
580 599 this.get_cell_element(index).after(cell.element);
581 600 };
582 601 cell.render();
583 602 this.select(this.find_cell_index(cell));
584 603 this.dirty = true;
585 604 return cell;
586 605 };
587 606 };
588 607 return cell;
589 608 };
590 609
591 610
592 611 Notebook.prototype.insert_cell_above = function (type, index) {
593 612 // type = ('code','html','markdown')
594 613 // index = cell index or undefined to insert above selected
595 614 index = this.index_or_selected(index);
596 615 var cell = null;
616 if (this.undelete_index !== null && index <= this.undelete_index) {
617 this.undelete_index = this.undelete_index + 1;
618 }
597 619 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
598 620 if (type === 'code') {
599 621 cell = new IPython.CodeCell(this.kernel);
600 622 cell.set_input_prompt();
601 623 } else if (type === 'markdown') {
602 624 cell = new IPython.MarkdownCell();
603 625 } else if (type === 'html') {
604 626 cell = new IPython.HTMLCell();
605 627 } else if (type === 'raw') {
606 628 cell = new IPython.RawCell();
607 629 } else if (type === 'heading') {
608 630 cell = new IPython.HeadingCell();
609 631 };
610 632 if (cell !== null) {
611 633 if (this.ncells() === 0) {
612 634 this.element.find('div.end_space').before(cell.element);
613 635 } else if (this.is_valid_cell_index(index)) {
614 636 this.get_cell_element(index).before(cell.element);
615 637 };
616 638 cell.render();
617 639 this.select(this.find_cell_index(cell));
618 640 this.dirty = true;
619 641 return cell;
620 642 };
621 643 };
622 644 return cell;
623 645 };
624 646
625 647
626 648 Notebook.prototype.to_code = function (index) {
627 649 var i = this.index_or_selected(index);
628 650 if (this.is_valid_cell_index(i)) {
629 651 var source_element = this.get_cell_element(i);
630 652 var source_cell = source_element.data("cell");
631 653 if (!(source_cell instanceof IPython.CodeCell)) {
632 654 target_cell = this.insert_cell_below('code',i);
633 655 var text = source_cell.get_text();
634 656 if (text === source_cell.placeholder) {
635 657 text = '';
636 658 }
637 659 target_cell.set_text(text);
638 660 // make this value the starting point, so that we can only undo
639 661 // to this state, instead of a blank cell
640 662 target_cell.code_mirror.clearHistory();
641 663 source_element.remove();
642 664 this.dirty = true;
643 665 };
644 666 };
645 667 };
646 668
647 669
648 670 Notebook.prototype.to_markdown = function (index) {
649 671 var i = this.index_or_selected(index);
650 672 if (this.is_valid_cell_index(i)) {
651 673 var source_element = this.get_cell_element(i);
652 674 var source_cell = source_element.data("cell");
653 675 if (!(source_cell instanceof IPython.MarkdownCell)) {
654 676 target_cell = this.insert_cell_below('markdown',i);
655 677 var text = source_cell.get_text();
656 678 if (text === source_cell.placeholder) {
657 679 text = '';
658 680 };
659 681 // The edit must come before the set_text.
660 682 target_cell.edit();
661 683 target_cell.set_text(text);
662 684 // make this value the starting point, so that we can only undo
663 685 // to this state, instead of a blank cell
664 686 target_cell.code_mirror.clearHistory();
665 687 source_element.remove();
666 688 this.dirty = true;
667 689 };
668 690 };
669 691 };
670 692
671 693
672 694 Notebook.prototype.to_html = function (index) {
673 695 var i = this.index_or_selected(index);
674 696 if (this.is_valid_cell_index(i)) {
675 697 var source_element = this.get_cell_element(i);
676 698 var source_cell = source_element.data("cell");
677 699 var target_cell = null;
678 700 if (!(source_cell instanceof IPython.HTMLCell)) {
679 701 target_cell = this.insert_cell_below('html',i);
680 702 var text = source_cell.get_text();
681 703 if (text === source_cell.placeholder) {
682 704 text = '';
683 705 };
684 706 // The edit must come before the set_text.
685 707 target_cell.edit();
686 708 target_cell.set_text(text);
687 709 // make this value the starting point, so that we can only undo
688 710 // to this state, instead of a blank cell
689 711 target_cell.code_mirror.clearHistory();
690 712 source_element.remove();
691 713 this.dirty = true;
692 714 };
693 715 };
694 716 };
695 717
696 718
697 719 Notebook.prototype.to_raw = function (index) {
698 720 var i = this.index_or_selected(index);
699 721 if (this.is_valid_cell_index(i)) {
700 722 var source_element = this.get_cell_element(i);
701 723 var source_cell = source_element.data("cell");
702 724 var target_cell = null;
703 725 if (!(source_cell instanceof IPython.RawCell)) {
704 726 target_cell = this.insert_cell_below('raw',i);
705 727 var text = source_cell.get_text();
706 728 if (text === source_cell.placeholder) {
707 729 text = '';
708 730 };
709 731 // The edit must come before the set_text.
710 732 target_cell.edit();
711 733 target_cell.set_text(text);
712 734 // make this value the starting point, so that we can only undo
713 735 // to this state, instead of a blank cell
714 736 target_cell.code_mirror.clearHistory();
715 737 source_element.remove();
716 738 this.dirty = true;
717 739 };
718 740 };
719 741 };
720 742
721 743
722 744 Notebook.prototype.to_heading = function (index, level) {
723 745 level = level || 1;
724 746 var i = this.index_or_selected(index);
725 747 if (this.is_valid_cell_index(i)) {
726 748 var source_element = this.get_cell_element(i);
727 749 var source_cell = source_element.data("cell");
728 750 var target_cell = null;
729 751 if (source_cell instanceof IPython.HeadingCell) {
730 752 source_cell.set_level(level);
731 753 } else {
732 754 target_cell = this.insert_cell_below('heading',i);
733 755 var text = source_cell.get_text();
734 756 if (text === source_cell.placeholder) {
735 757 text = '';
736 758 };
737 759 // The edit must come before the set_text.
738 760 target_cell.set_level(level);
739 761 target_cell.edit();
740 762 target_cell.set_text(text);
741 763 // make this value the starting point, so that we can only undo
742 764 // to this state, instead of a blank cell
743 765 target_cell.code_mirror.clearHistory();
744 766 source_element.remove();
745 767 this.dirty = true;
746 768 };
747 769 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
748 770 {'cell_type':'heading',level:level}
749 771 );
750 772 };
751 773 };
752 774
753 775
754 776 // Cut/Copy/Paste
755 777
756 778 Notebook.prototype.enable_paste = function () {
757 779 var that = this;
758 780 if (!this.paste_enabled) {
759 $('#paste_cell').removeClass('ui-state-disabled')
760 .on('click', function () {that.paste_cell();});
781 $('#paste_cell_replace').removeClass('ui-state-disabled')
782 .on('click', function () {that.paste_cell_replace();});
761 783 $('#paste_cell_above').removeClass('ui-state-disabled')
762 784 .on('click', function () {that.paste_cell_above();});
763 785 $('#paste_cell_below').removeClass('ui-state-disabled')
764 786 .on('click', function () {that.paste_cell_below();});
765 787 this.paste_enabled = true;
766 788 };
767 789 };
768 790
769 791
770 792 Notebook.prototype.disable_paste = function () {
771 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 795 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
774 796 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
775 797 this.paste_enabled = false;
776 798 };
777 799 };
778 800
779 801
780 802 Notebook.prototype.cut_cell = function () {
781 803 this.copy_cell();
782 804 this.delete_cell();
783 805 }
784 806
785 807 Notebook.prototype.copy_cell = function () {
786 808 var cell = this.get_selected_cell();
787 809 this.clipboard = cell.toJSON();
788 810 this.enable_paste();
789 811 };
790 812
791 813
792 Notebook.prototype.paste_cell = function () {
814 Notebook.prototype.paste_cell_replace = function () {
793 815 if (this.clipboard !== null && this.paste_enabled) {
794 816 var cell_data = this.clipboard;
795 817 var new_cell = this.insert_cell_above(cell_data.cell_type);
796 818 new_cell.fromJSON(cell_data);
797 819 old_cell = this.get_next_cell(new_cell);
798 820 this.delete_cell(this.find_cell_index(old_cell));
799 821 this.select(this.find_cell_index(new_cell));
800 822 };
801 823 };
802 824
803 825
804 826 Notebook.prototype.paste_cell_above = function () {
805 827 if (this.clipboard !== null && this.paste_enabled) {
806 828 var cell_data = this.clipboard;
807 829 var new_cell = this.insert_cell_above(cell_data.cell_type);
808 830 new_cell.fromJSON(cell_data);
809 831 };
810 832 };
811 833
812 834
813 835 Notebook.prototype.paste_cell_below = function () {
814 836 if (this.clipboard !== null && this.paste_enabled) {
815 837 var cell_data = this.clipboard;
816 838 var new_cell = this.insert_cell_below(cell_data.cell_type);
817 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 871 // Split/merge
823 872
824 873 Notebook.prototype.split_cell = function () {
825 874 // Todo: implement spliting for other cell types.
826 875 var cell = this.get_selected_cell();
827 876 if (cell.is_splittable()) {
828 877 texta = cell.get_pre_cursor();
829 878 textb = cell.get_post_cursor();
830 879 if (cell instanceof IPython.CodeCell) {
831 880 cell.set_text(texta);
832 881 var new_cell = this.insert_cell_below('code');
833 882 new_cell.set_text(textb);
834 883 } else if (cell instanceof IPython.MarkdownCell) {
835 884 cell.set_text(texta);
836 885 cell.render();
837 886 var new_cell = this.insert_cell_below('markdown');
838 887 new_cell.edit(); // editor must be visible to call set_text
839 888 new_cell.set_text(textb);
840 889 new_cell.render();
841 890 } else if (cell instanceof IPython.HTMLCell) {
842 891 cell.set_text(texta);
843 892 cell.render();
844 893 var new_cell = this.insert_cell_below('html');
845 894 new_cell.edit(); // editor must be visible to call set_text
846 895 new_cell.set_text(textb);
847 896 new_cell.render();
848 897 };
849 898 };
850 899 };
851 900
852 901
853 902 Notebook.prototype.merge_cell_above = function () {
854 903 var index = this.get_selected_index();
855 904 var cell = this.get_cell(index);
856 905 if (index > 0) {
857 906 upper_cell = this.get_cell(index-1);
858 907 upper_text = upper_cell.get_text();
859 908 text = cell.get_text();
860 909 if (cell instanceof IPython.CodeCell) {
861 910 cell.set_text(upper_text+'\n'+text);
862 911 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
863 912 cell.edit();
864 913 cell.set_text(upper_text+'\n'+text);
865 914 cell.render();
866 915 };
867 916 this.delete_cell(index-1);
868 917 this.select(this.find_cell_index(cell));
869 918 };
870 919 };
871 920
872 921
873 922 Notebook.prototype.merge_cell_below = function () {
874 923 var index = this.get_selected_index();
875 924 var cell = this.get_cell(index);
876 925 if (index < this.ncells()-1) {
877 926 lower_cell = this.get_cell(index+1);
878 927 lower_text = lower_cell.get_text();
879 928 text = cell.get_text();
880 929 if (cell instanceof IPython.CodeCell) {
881 930 cell.set_text(text+'\n'+lower_text);
882 931 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
883 932 cell.edit();
884 933 cell.set_text(text+'\n'+lower_text);
885 934 cell.render();
886 935 };
887 936 this.delete_cell(index+1);
888 937 this.select(this.find_cell_index(cell));
889 938 };
890 939 };
891 940
892 941
893 942 // Cell collapsing and output clearing
894 943
895 944 Notebook.prototype.collapse = function (index) {
896 945 var i = this.index_or_selected(index);
897 946 this.get_cell(i).collapse();
898 947 this.dirty = true;
899 948 };
900 949
901 950
902 951 Notebook.prototype.expand = function (index) {
903 952 var i = this.index_or_selected(index);
904 953 this.get_cell(i).expand();
905 954 this.dirty = true;
906 955 };
907 956
908 957
909 958 Notebook.prototype.toggle_output = function (index) {
910 959 var i = this.index_or_selected(index);
911 960 this.get_cell(i).toggle_output();
912 961 this.dirty = true;
913 962 };
914 963
915 964
916 965 Notebook.prototype.toggle_output_scroll = function (index) {
917 966 var i = this.index_or_selected(index);
918 967 this.get_cell(i).toggle_output_scroll();
919 968 };
920 969
921 970
922 971 Notebook.prototype.collapse_all_output = function () {
923 972 var ncells = this.ncells();
924 973 var cells = this.get_cells();
925 974 for (var i=0; i<ncells; i++) {
926 975 if (cells[i] instanceof IPython.CodeCell) {
927 976 cells[i].output_area.collapse();
928 977 }
929 978 };
930 979 // this should not be set if the `collapse` key is removed from nbformat
931 980 this.dirty = true;
932 981 };
933 982
934 983
935 984 Notebook.prototype.scroll_all_output = function () {
936 985 var ncells = this.ncells();
937 986 var cells = this.get_cells();
938 987 for (var i=0; i<ncells; i++) {
939 988 if (cells[i] instanceof IPython.CodeCell) {
940 989 cells[i].output_area.expand();
941 990 cells[i].output_area.scroll_if_long(20);
942 991 }
943 992 };
944 993 // this should not be set if the `collapse` key is removed from nbformat
945 994 this.dirty = true;
946 995 };
947 996
948 997
949 998 Notebook.prototype.expand_all_output = function () {
950 999 var ncells = this.ncells();
951 1000 var cells = this.get_cells();
952 1001 for (var i=0; i<ncells; i++) {
953 1002 if (cells[i] instanceof IPython.CodeCell) {
954 1003 cells[i].output_area.expand();
955 1004 cells[i].output_area.unscroll_area();
956 1005 }
957 1006 };
958 1007 // this should not be set if the `collapse` key is removed from nbformat
959 1008 this.dirty = true;
960 1009 };
961 1010
962 1011
963 1012 Notebook.prototype.clear_all_output = function () {
964 1013 var ncells = this.ncells();
965 1014 var cells = this.get_cells();
966 1015 for (var i=0; i<ncells; i++) {
967 1016 if (cells[i] instanceof IPython.CodeCell) {
968 1017 cells[i].clear_output(true,true,true);
969 1018 // Make all In[] prompts blank, as well
970 1019 // TODO: make this configurable (via checkbox?)
971 1020 cells[i].set_input_prompt();
972 1021 }
973 1022 };
974 1023 this.dirty = true;
975 1024 };
976 1025
977 1026
978 1027 // Other cell functions: line numbers, ...
979 1028
980 1029 Notebook.prototype.cell_toggle_line_numbers = function() {
981 1030 this.get_selected_cell().toggle_line_numbers();
982 1031 };
983 1032
984 1033 // Kernel related things
985 1034
986 1035 Notebook.prototype.start_kernel = function () {
987 1036 var base_url = $('body').data('baseKernelUrl') + "kernels";
988 1037 this.kernel = new IPython.Kernel(base_url);
989 1038 this.kernel.start(this.notebook_id);
990 1039 // Now that the kernel has been created, tell the CodeCells about it.
991 1040 var ncells = this.ncells();
992 1041 for (var i=0; i<ncells; i++) {
993 1042 var cell = this.get_cell(i);
994 1043 if (cell instanceof IPython.CodeCell) {
995 1044 cell.set_kernel(this.kernel)
996 1045 };
997 1046 };
998 1047 };
999 1048
1000 1049
1001 1050 Notebook.prototype.restart_kernel = function () {
1002 1051 var that = this;
1003 1052 var dialog = $('<div/>');
1004 1053 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
1005 1054 $(document).append(dialog);
1006 1055 dialog.dialog({
1007 1056 resizable: false,
1008 1057 modal: true,
1009 1058 title: "Restart kernel or continue running?",
1010 1059 closeText: '',
1011 1060 buttons : {
1012 1061 "Restart": function () {
1013 1062 that.kernel.restart();
1014 1063 $(this).dialog('close');
1015 1064 },
1016 1065 "Continue running": function () {
1017 1066 $(this).dialog('close');
1018 1067 }
1019 1068 }
1020 1069 });
1021 1070 };
1022 1071
1023 1072
1024 1073 Notebook.prototype.execute_selected_cell = function (options) {
1025 1074 // add_new: should a new cell be added if we are at the end of the nb
1026 1075 // terminal: execute in terminal mode, which stays in the current cell
1027 1076 default_options = {terminal: false, add_new: true};
1028 1077 $.extend(default_options, options);
1029 1078 var that = this;
1030 1079 var cell = that.get_selected_cell();
1031 1080 var cell_index = that.find_cell_index(cell);
1032 1081 if (cell instanceof IPython.CodeCell) {
1033 1082 cell.execute();
1034 1083 } else if (cell instanceof IPython.HTMLCell) {
1035 1084 cell.render();
1036 1085 }
1037 1086 if (default_options.terminal) {
1038 1087 cell.select_all();
1039 1088 } else {
1040 1089 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1041 1090 that.insert_cell_below('code');
1042 1091 // If we are adding a new cell at the end, scroll down to show it.
1043 1092 that.scroll_to_bottom();
1044 1093 } else {
1045 1094 that.select(cell_index+1);
1046 1095 };
1047 1096 };
1048 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 1110 Notebook.prototype.execute_all_cells = function () {
1053 var ncells = this.ncells();
1054 for (var i=0; i<ncells; i++) {
1111 this.execute_cell_range(0, this.ncells());
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 1117 this.select(i);
1056 1118 this.execute_selected_cell({add_new:false});
1057 1119 };
1058 this.scroll_to_bottom();
1059 1120 };
1060 1121
1061 1122 // Persistance and loading
1062 1123
1063 1124 Notebook.prototype.get_notebook_id = function () {
1064 1125 return this.notebook_id;
1065 1126 };
1066 1127
1067 1128
1068 1129 Notebook.prototype.get_notebook_name = function () {
1069 1130 return this.notebook_name;
1070 1131 };
1071 1132
1072 1133
1073 1134 Notebook.prototype.set_notebook_name = function (name) {
1074 1135 this.notebook_name = name;
1075 1136 };
1076 1137
1077 1138
1078 1139 Notebook.prototype.test_notebook_name = function (nbname) {
1079 1140 nbname = nbname || '';
1080 1141 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1081 1142 return true;
1082 1143 } else {
1083 1144 return false;
1084 1145 };
1085 1146 };
1086 1147
1087 1148
1088 1149 Notebook.prototype.fromJSON = function (data) {
1089 1150 var ncells = this.ncells();
1090 1151 var i;
1091 1152 for (i=0; i<ncells; i++) {
1092 1153 // Always delete cell 0 as they get renumbered as they are deleted.
1093 1154 this.delete_cell(0);
1094 1155 };
1095 1156 // Save the metadata and name.
1096 1157 this.metadata = data.metadata;
1097 1158 this.notebook_name = data.metadata.name;
1098 1159 // Only handle 1 worksheet for now.
1099 1160 var worksheet = data.worksheets[0];
1100 1161 if (worksheet !== undefined) {
1101 1162 if (worksheet.metadata) {
1102 1163 this.worksheet_metadata = worksheet.metadata;
1103 1164 }
1104 1165 var new_cells = worksheet.cells;
1105 1166 ncells = new_cells.length;
1106 1167 var cell_data = null;
1107 1168 var new_cell = null;
1108 1169 for (i=0; i<ncells; i++) {
1109 1170 cell_data = new_cells[i];
1110 1171 // VERSIONHACK: plaintext -> raw
1111 1172 // handle never-released plaintext name for raw cells
1112 1173 if (cell_data.cell_type === 'plaintext'){
1113 1174 cell_data.cell_type = 'raw';
1114 1175 }
1115 1176
1116 1177 new_cell = this.insert_cell_below(cell_data.cell_type);
1117 1178 new_cell.fromJSON(cell_data);
1118 1179 };
1119 1180 };
1120 1181 if (data.worksheets.length > 1) {
1121 1182 var dialog = $('<div/>');
1122 1183 dialog.html("This notebook has " + data.worksheets.length + " worksheets, " +
1123 1184 "but this version of IPython can only handle the first. " +
1124 1185 "If you save this notebook, worksheets after the first will be lost."
1125 1186 );
1126 1187 this.element.append(dialog);
1127 1188 dialog.dialog({
1128 1189 resizable: false,
1129 1190 modal: true,
1130 1191 title: "Multiple worksheets",
1131 1192 closeText: "",
1132 1193 close: function(event, ui) {$(this).dialog('destroy').remove();},
1133 1194 buttons : {
1134 1195 "OK": function () {
1135 1196 $(this).dialog('close');
1136 1197 }
1137 1198 },
1138 1199 width: 400
1139 1200 });
1140 1201 }
1141 1202 };
1142 1203
1143 1204
1144 1205 Notebook.prototype.toJSON = function () {
1145 1206 var cells = this.get_cells();
1146 1207 var ncells = cells.length;
1147 1208 var cell_array = new Array(ncells);
1148 1209 for (var i=0; i<ncells; i++) {
1149 1210 cell_array[i] = cells[i].toJSON();
1150 1211 };
1151 1212 var data = {
1152 1213 // Only handle 1 worksheet for now.
1153 1214 worksheets : [{
1154 1215 cells: cell_array,
1155 1216 metadata: this.worksheet_metadata
1156 1217 }],
1157 1218 metadata : this.metadata
1158 1219 };
1159 1220 return data;
1160 1221 };
1161 1222
1162 1223 Notebook.prototype.save_notebook = function () {
1163 1224 // We may want to move the name/id/nbformat logic inside toJSON?
1164 1225 var data = this.toJSON();
1165 1226 data.metadata.name = this.notebook_name;
1166 1227 data.nbformat = this.nbformat;
1167 1228 data.nbformat_minor = this.nbformat_minor;
1168 1229 // We do the call with settings so we can set cache to false.
1169 1230 var settings = {
1170 1231 processData : false,
1171 1232 cache : false,
1172 1233 type : "PUT",
1173 1234 data : JSON.stringify(data),
1174 1235 headers : {'Content-Type': 'application/json'},
1175 1236 success : $.proxy(this.save_notebook_success,this),
1176 1237 error : $.proxy(this.save_notebook_error,this)
1177 1238 };
1178 1239 $([IPython.events]).trigger('notebook_saving.Notebook');
1179 1240 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1180 1241 $.ajax(url, settings);
1181 1242 };
1182 1243
1183 1244
1184 1245 Notebook.prototype.save_notebook_success = function (data, status, xhr) {
1185 1246 this.dirty = false;
1186 1247 $([IPython.events]).trigger('notebook_saved.Notebook');
1187 1248 };
1188 1249
1189 1250
1190 1251 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1191 1252 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1192 1253 };
1193 1254
1194 1255
1195 1256 Notebook.prototype.load_notebook = function (notebook_id) {
1196 1257 var that = this;
1197 1258 this.notebook_id = notebook_id;
1198 1259 // We do the call with settings so we can set cache to false.
1199 1260 var settings = {
1200 1261 processData : false,
1201 1262 cache : false,
1202 1263 type : "GET",
1203 1264 dataType : "json",
1204 1265 success : $.proxy(this.load_notebook_success,this),
1205 1266 error : $.proxy(this.load_notebook_error,this),
1206 1267 };
1207 1268 $([IPython.events]).trigger('notebook_loading.Notebook');
1208 1269 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1209 1270 $.ajax(url, settings);
1210 1271 };
1211 1272
1212 1273
1213 1274 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1214 1275 this.fromJSON(data);
1215 1276 if (this.ncells() === 0) {
1216 1277 this.insert_cell_below('code');
1217 1278 };
1218 1279 this.dirty = false;
1219 1280 this.select(0);
1220 1281 this.scroll_to_top();
1221 1282 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1222 1283 msg = "This notebook has been converted from an older " +
1223 1284 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1224 1285 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1225 1286 "newer notebook format will be used and older verions of IPython " +
1226 1287 "may not be able to read it. To keep the older version, close the " +
1227 1288 "notebook without saving it.";
1228 1289 var dialog = $('<div/>');
1229 1290 dialog.html(msg);
1230 1291 this.element.append(dialog);
1231 1292 dialog.dialog({
1232 1293 resizable: false,
1233 1294 modal: true,
1234 1295 title: "Notebook converted",
1235 1296 closeText: "",
1236 1297 close: function(event, ui) {$(this).dialog('destroy').remove();},
1237 1298 buttons : {
1238 1299 "OK": function () {
1239 1300 $(this).dialog('close');
1240 1301 }
1241 1302 },
1242 1303 width: 400
1243 1304 });
1244 1305 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1245 1306 var that = this;
1246 1307 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1247 1308 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1248 1309 msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1249 1310 this_vs + ". You can still work with this notebook, but some features " +
1250 1311 "introduced in later notebook versions may not be available."
1251 1312
1252 1313 var dialog = $('<div/>');
1253 1314 dialog.html(msg);
1254 1315 this.element.append(dialog);
1255 1316 dialog.dialog({
1256 1317 resizable: false,
1257 1318 modal: true,
1258 1319 title: "Newer Notebook",
1259 1320 closeText: "",
1260 1321 close: function(event, ui) {$(this).dialog('destroy').remove();},
1261 1322 buttons : {
1262 1323 "OK": function () {
1263 1324 $(this).dialog('close');
1264 1325 }
1265 1326 },
1266 1327 width: 400
1267 1328 });
1268 1329
1269 1330 }
1270 1331 // Create the kernel after the notebook is completely loaded to prevent
1271 1332 // code execution upon loading, which is a security risk.
1272 1333 if (! this.read_only) {
1273 1334 this.start_kernel();
1274 1335 }
1275 1336 $([IPython.events]).trigger('notebook_loaded.Notebook');
1276 1337 };
1277 1338
1278 1339
1279 1340 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1280 1341 if (xhr.status === 500) {
1281 1342 msg = "An error occurred while loading this notebook. Most likely " +
1282 1343 "this notebook is in a newer format than is supported by this " +
1283 1344 "version of IPython. This version can load notebook formats " +
1284 1345 "v"+this.nbformat+" or earlier.";
1285 1346 var dialog = $('<div/>');
1286 1347 dialog.html(msg);
1287 1348 this.element.append(dialog);
1288 1349 dialog.dialog({
1289 1350 resizable: false,
1290 1351 modal: true,
1291 1352 title: "Error loading notebook",
1292 1353 closeText: "",
1293 1354 close: function(event, ui) {$(this).dialog('destroy').remove();},
1294 1355 buttons : {
1295 1356 "OK": function () {
1296 1357 $(this).dialog('close');
1297 1358 }
1298 1359 },
1299 1360 width: 400
1300 1361 });
1301 1362 }
1302 1363 }
1303 1364
1304 1365 IPython.Notebook = Notebook;
1305 1366
1306 1367
1307 1368 return IPython;
1308 1369
1309 1370 }(IPython));
1310 1371
@@ -1,557 +1,559 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // OutputArea
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13 "use strict";
14 14
15 15 var utils = IPython.utils;
16 16
17 17 var OutputArea = function (selector, prompt_area) {
18 18 this.selector = selector;
19 19 this.wrapper = $(selector);
20 20 this.outputs = [];
21 21 this.collapsed = false;
22 22 this.scrolled = false;
23 23 this.clear_out_timeout = null;
24 24 if (prompt_area === undefined) {
25 25 this.prompt_area = true;
26 26 } else {
27 27 this.prompt_area = prompt_area;
28 28 };
29 29 this.create_elements();
30 30 this.style();
31 31 this.bind_events();
32 32 };
33 33
34 34 OutputArea.prototype.create_elements = function () {
35 35 this.element = $("<div/>");
36 36 this.collapse_button = $("<div/>");
37 37 this.prompt_overlay = $("<div/>");
38 38 this.wrapper.append(this.prompt_overlay);
39 39 this.wrapper.append(this.element);
40 40 this.wrapper.append(this.collapse_button);
41 41 };
42 42
43 43
44 44 OutputArea.prototype.style = function () {
45 45 this.collapse_button.hide();
46 46 this.prompt_overlay.hide();
47 47
48 48 this.wrapper.addClass('output_wrapper');
49 49 this.element.addClass('output vbox');
50 50
51 51 this.collapse_button.button();
52 52 this.collapse_button.addClass('output_collapsed vbox');
53 53 this.collapse_button.attr('title', 'click to expand outout');
54 54 this.collapse_button.html('. . .');
55 55
56 56 this.prompt_overlay.addClass('out_prompt_overlay prompt');
57 57 this.prompt_overlay.attr('title', 'click to expand outout; double click to hide output');
58 58
59 59 this.collapse();
60 60 };
61 61
62 62
63 63 OutputArea.prototype._should_scroll = function (lines) {
64 64 if (!lines) {
65 65 lines = 100;
66 66 }
67 67 // line-height from http://stackoverflow.com/questions/1185151
68 68 var fontSize = this.element.css('font-size');
69 69 var lineHeight = Math.floor(parseInt(fontSize.replace('px','')) * 1.5);
70 70
71 71 return (this.element.height() > lines * lineHeight);
72 72 };
73 73
74 74
75 75 OutputArea.prototype.bind_events = function () {
76 76 var that = this;
77 77 this.prompt_overlay.dblclick(function () { that.toggle_output(); });
78 78 this.prompt_overlay.click(function () { that.toggle_scroll(); });
79 79
80 80 this.element.resize(function () {
81 81 // FIXME: Firefox on Linux misbehaves, so automatic scrolling is disabled
82 82 if ( $.browser.mozilla ) {
83 83 return;
84 84 }
85 85 // maybe scroll output,
86 86 // if it's grown large enough and hasn't already been scrolled.
87 87 if ( !that.scrolled && that._should_scroll()) {
88 88 that.scroll_area();
89 89 }
90 90 });
91 91 this.collapse_button.click(function () {
92 92 that.expand();
93 93 });
94 94 this.collapse_button.hover(function () {
95 95 $(this).addClass("ui-state-hover");
96 96 }, function () {
97 97 $(this).removeClass("ui-state-hover");
98 98 });
99 99 };
100 100
101 101
102 102 OutputArea.prototype.collapse = function () {
103 103 if (!this.collapsed) {
104 104 this.element.hide();
105 105 this.prompt_overlay.hide();
106 106 if (this.element.html()){
107 107 this.collapse_button.show();
108 108 }
109 109 this.collapsed = true;
110 110 };
111 111 };
112 112
113 113
114 114 OutputArea.prototype.expand = function () {
115 115 if (this.collapsed) {
116 116 this.collapse_button.hide();
117 117 this.element.show();
118 118 this.prompt_overlay.show();
119 119 this.collapsed = false;
120 120 };
121 121 };
122 122
123 123
124 124 OutputArea.prototype.toggle_output = function () {
125 125 if (this.collapsed) {
126 126 this.expand();
127 127 } else {
128 128 this.collapse();
129 129 };
130 130 };
131 131
132 132
133 133 OutputArea.prototype.scroll_area = function () {
134 134 this.element.addClass('output_scroll');
135 135 this.prompt_overlay.attr('title', 'click to unscroll output; double click to hide');
136 136 this.scrolled = true;
137 137 };
138 138
139 139
140 140 OutputArea.prototype.unscroll_area = function () {
141 141 this.element.removeClass('output_scroll');
142 142 this.prompt_overlay.attr('title', 'click to scroll output; double click to hide');
143 143 this.scrolled = false;
144 144 };
145 145
146 146
147 147 OutputArea.prototype.scroll_if_long = function (lines) {
148 148 if (this._should_scroll(lines)) {
149 149 // only allow scrolling long-enough output
150 150 this.scroll_area();
151 151 };
152 152 };
153 153
154 154
155 155 OutputArea.prototype.toggle_scroll = function () {
156 156 if (this.scrolled) {
157 157 this.unscroll_area();
158 158 } else {
159 159 // only allow scrolling long-enough output
160 160 this.scroll_if_long(20);
161 161 };
162 162 };
163 163
164 164
165 165 // typeset with MathJax if MathJax is available
166 166 OutputArea.prototype.typeset = function () {
167 167 if (window.MathJax){
168 168 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
169 169 }
170 170 };
171 171
172 172
173 173 OutputArea.prototype.handle_output = function (msg_type, content) {
174 174 var json = {};
175 175 json.output_type = msg_type;
176 176 if (msg_type === "stream") {
177 177 json.text = content.data;
178 178 json.stream = content.name;
179 179 } else if (msg_type === "display_data") {
180 180 json = this.convert_mime_types(json, content.data);
181 181 } else if (msg_type === "pyout") {
182 182 json.prompt_number = content.execution_count;
183 183 json = this.convert_mime_types(json, content.data);
184 184 } else if (msg_type === "pyerr") {
185 185 json.ename = content.ename;
186 186 json.evalue = content.evalue;
187 187 json.traceback = content.traceback;
188 188 };
189 189 // append with dynamic=true
190 190 this.append_output(json, true);
191 191 };
192 192
193 193
194 194 OutputArea.prototype.convert_mime_types = function (json, data) {
195 195 if (data['text/plain'] !== undefined) {
196 196 json.text = data['text/plain'];
197 197 };
198 198 if (data['text/html'] !== undefined) {
199 199 json.html = data['text/html'];
200 200 };
201 201 if (data['image/svg+xml'] !== undefined) {
202 202 json.svg = data['image/svg+xml'];
203 203 };
204 204 if (data['image/png'] !== undefined) {
205 205 json.png = data['image/png'];
206 206 };
207 207 if (data['image/jpeg'] !== undefined) {
208 208 json.jpeg = data['image/jpeg'];
209 209 };
210 210 if (data['text/latex'] !== undefined) {
211 211 json.latex = data['text/latex'];
212 212 };
213 213 if (data['application/json'] !== undefined) {
214 214 json.json = data['application/json'];
215 215 };
216 216 if (data['application/javascript'] !== undefined) {
217 217 json.javascript = data['application/javascript'];
218 218 }
219 219 return json;
220 220 };
221 221
222 222
223 223 OutputArea.prototype.append_output = function (json, dynamic) {
224 224 // If dynamic is true, javascript output will be eval'd.
225 225 this.expand();
226 226 this.flush_clear_timeout();
227 227 if (json.output_type === 'pyout') {
228 228 this.append_pyout(json, dynamic);
229 229 } else if (json.output_type === 'pyerr') {
230 230 this.append_pyerr(json);
231 231 } else if (json.output_type === 'display_data') {
232 232 this.append_display_data(json, dynamic);
233 233 } else if (json.output_type === 'stream') {
234 234 this.append_stream(json);
235 235 };
236 236 this.outputs.push(json);
237 237 var that = this;
238 238 setTimeout(function(){that.element.trigger('resize');}, 100);
239 239 };
240 240
241 241
242 242 OutputArea.prototype.create_output_area = function () {
243 243 var oa = $("<div/>").addClass("hbox output_area");
244 244 if (this.prompt_area) {
245 245 oa.append($('<div/>').addClass('prompt'));
246 246 }
247 247 return oa;
248 248 };
249 249
250 250
251 251 OutputArea.prototype.append_pyout = function (json, dynamic) {
252 252 var n = json.prompt_number || ' ';
253 253 var toinsert = this.create_output_area();
254 254 if (this.prompt_area) {
255 255 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
256 256 }
257 257 this.append_mime_type(json, toinsert, dynamic);
258 258 this.element.append(toinsert);
259 259 // If we just output latex, typeset it.
260 260 if ((json.latex !== undefined) || (json.html !== undefined)) {
261 261 this.typeset();
262 262 };
263 263 };
264 264
265 265
266 266 OutputArea.prototype.append_pyerr = function (json) {
267 267 var tb = json.traceback;
268 268 if (tb !== undefined && tb.length > 0) {
269 269 var s = '';
270 270 var len = tb.length;
271 271 for (var i=0; i<len; i++) {
272 272 s = s + tb[i] + '\n';
273 273 }
274 274 s = s + '\n';
275 275 var toinsert = this.create_output_area();
276 276 this.append_text(s, toinsert);
277 277 this.element.append(toinsert);
278 278 };
279 279 };
280 280
281 281
282 282 OutputArea.prototype.append_stream = function (json) {
283 283 // temporary fix: if stream undefined (json file written prior to this patch),
284 284 // default to most likely stdout:
285 285 if (json.stream == undefined){
286 286 json.stream = 'stdout';
287 287 }
288 288 var text = json.text;
289 289 var subclass = "output_"+json.stream;
290 290 if (this.outputs.length > 0){
291 291 // have at least one output to consider
292 292 var last = this.outputs[this.outputs.length-1];
293 293 if (last.output_type == 'stream' && json.stream == last.stream){
294 294 // latest output was in the same stream,
295 295 // so append directly into its pre tag
296 296 // escape ANSI & HTML specials:
297 297 var pre = this.element.find('div.'+subclass).last().find('pre');
298 298 var html = utils.fixCarriageReturn(
299 299 pre.html() + utils.fixConsole(text));
300 300 pre.html(html);
301 301 return;
302 302 }
303 303 }
304 304
305 305 if (!text.replace("\r", "")) {
306 306 // text is nothing (empty string, \r, etc.)
307 307 // so don't append any elements, which might add undesirable space
308 308 return;
309 309 }
310 310
311 311 // If we got here, attach a new div
312 312 var toinsert = this.create_output_area();
313 313 this.append_text(text, toinsert, "output_stream "+subclass);
314 314 this.element.append(toinsert);
315 315 };
316 316
317 317
318 318 OutputArea.prototype.append_display_data = function (json, dynamic) {
319 319 var toinsert = this.create_output_area();
320 320 this.append_mime_type(json, toinsert, dynamic);
321 321 this.element.append(toinsert);
322 322 // If we just output latex, typeset it.
323 323 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
324 324 this.typeset();
325 325 };
326 326 };
327 327
328 328
329 329 OutputArea.prototype.append_mime_type = function (json, element, dynamic) {
330 330 if (json.javascript !== undefined && dynamic) {
331 331 this.append_javascript(json.javascript, element, dynamic);
332 332 } else if (json.html !== undefined) {
333 333 this.append_html(json.html, element);
334 334 } else if (json.latex !== undefined) {
335 335 this.append_latex(json.latex, element);
336 336 } else if (json.svg !== undefined) {
337 337 this.append_svg(json.svg, element);
338 338 } else if (json.png !== undefined) {
339 339 this.append_png(json.png, element);
340 340 } else if (json.jpeg !== undefined) {
341 341 this.append_jpeg(json.jpeg, element);
342 342 } else if (json.text !== undefined) {
343 343 this.append_text(json.text, element);
344 344 };
345 345 };
346 346
347 347
348 348 OutputArea.prototype.append_html = function (html, element) {
349 349 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_html rendered_html");
350 350 toinsert.append(html);
351 351 element.append(toinsert);
352 352 };
353 353
354 354
355 355 OutputArea.prototype.append_javascript = function (js, container) {
356 356 // We just eval the JS code, element appears in the local scope.
357 357 var element = $("<div/>").addClass("box-flex1 output_subarea");
358 358 container.append(element);
359 359 // Div for js shouldn't be drawn, as it will add empty height to the area.
360 360 container.hide();
361 361 // If the Javascript appends content to `element` that should be drawn, then
362 362 // it must also call `container.show()`.
363 363 try {
364 364 eval(js);
365 365 } catch(err) {
366 366 console.log('Error in Javascript!');
367 367 console.log(err);
368 368 container.show();
369 369 element.append($('<div/>')
370 370 .html("Error in Javascript !<br/>"+
371 371 err.toString()+
372 372 '<br/>See your browser Javascript console for more details.')
373 373 .addClass('js-error')
374 374 );
375 375 }
376 376 }
377 377
378 378
379 379 OutputArea.prototype.append_text = function (data, element, extra_class) {
380 380 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_text");
381 381 // escape ANSI & HTML specials in plaintext:
382 data = utils.wrapUrls(data);
382 383 data = utils.fixConsole(data);
383 384 data = utils.fixCarriageReturn(data);
385 data = utils.autoLinkUrls(data);
384 386 if (extra_class){
385 387 toinsert.addClass(extra_class);
386 388 }
387 389 toinsert.append($("<pre/>").html(data));
388 390 element.append(toinsert);
389 391 };
390 392
391 393
392 394 OutputArea.prototype.append_svg = function (svg, element) {
393 395 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_svg");
394 396 toinsert.append(svg);
395 397 element.append(toinsert);
396 398 };
397 399
398 400
399 401 OutputArea.prototype._dblclick_to_reset_size = function (img) {
400 402 // schedule wrapping image in resizable after a delay,
401 403 // so we don't end up calling resize on a zero-size object
402 404 var that = this;
403 405 setTimeout(function () {
404 406 var h0 = img.height();
405 407 var w0 = img.width();
406 408 if (!(h0 && w0)) {
407 409 // zero size, schedule another timeout
408 410 that._dblclick_to_reset_size(img);
409 411 return
410 412 }
411 413 img.resizable({
412 414 aspectRatio: true,
413 415 autoHide: true
414 416 });
415 417 img.dblclick(function () {
416 418 // resize wrapper & image together for some reason:
417 419 img.parent().height(h0);
418 420 img.height(h0);
419 421 img.parent().width(w0);
420 422 img.width(w0);
421 423 });
422 424 }, 250);
423 425 }
424 426
425 427
426 428 OutputArea.prototype.append_png = function (png, element) {
427 429 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_png");
428 430 var img = $("<img/>").attr('src','data:image/png;base64,'+png);
429 431 this._dblclick_to_reset_size(img);
430 432 toinsert.append(img);
431 433 element.append(toinsert);
432 434 };
433 435
434 436
435 437 OutputArea.prototype.append_jpeg = function (jpeg, element) {
436 438 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_jpeg");
437 439 var img = $("<img/>").attr('src','data:image/jpeg;base64,'+jpeg);
438 440 this._dblclick_to_reset_size(img);
439 441 toinsert.append(img);
440 442 element.append(toinsert);
441 443 };
442 444
443 445
444 446 OutputArea.prototype.append_latex = function (latex, element) {
445 447 // This method cannot do the typesetting because the latex first has to
446 448 // be on the page.
447 449 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_latex");
448 450 toinsert.append(latex);
449 451 element.append(toinsert);
450 452 };
451 453
452 454
453 455 OutputArea.prototype.handle_clear_output = function (content) {
454 456 this.clear_output(content.stdout, content.stderr, content.other);
455 457 }
456 458
457 459
458 460 OutputArea.prototype.clear_output = function (stdout, stderr, other) {
459 461 var that = this;
460 462 if (this.clear_out_timeout != null){
461 463 // fire previous pending clear *immediately*
462 464 clearTimeout(this.clear_out_timeout);
463 465 this.clear_out_timeout = null;
464 466 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
465 467 }
466 468 // store flags for flushing the timeout
467 469 this._clear_stdout = stdout;
468 470 this._clear_stderr = stderr;
469 471 this._clear_other = other;
470 472 this.clear_out_timeout = setTimeout(function() {
471 473 // really clear timeout only after a short delay
472 474 // this reduces flicker in 'clear_output; print' cases
473 475 that.clear_out_timeout = null;
474 476 that._clear_stdout = that._clear_stderr = that._clear_other = null;
475 477 that.clear_output_callback(stdout, stderr, other);
476 478 }, 500
477 479 );
478 480 };
479 481
480 482
481 483 OutputArea.prototype.clear_output_callback = function (stdout, stderr, other) {
482 484 var output_div = this.element;
483 485
484 486 if (stdout && stderr && other){
485 487 // clear all, no need for logic
486 488 output_div.html("");
487 489 this.outputs = [];
488 490 this.unscroll_area();
489 491 return;
490 492 }
491 493 // remove html output
492 494 // each output_subarea that has an identifying class is in an output_area
493 495 // which is the element to be removed.
494 496 if (stdout) {
495 497 output_div.find("div.output_stdout").parent().remove();
496 498 }
497 499 if (stderr) {
498 500 output_div.find("div.output_stderr").parent().remove();
499 501 }
500 502 if (other) {
501 503 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
502 504 }
503 505 this.unscroll_area();
504 506
505 507 // remove cleared outputs from JSON list:
506 508 for (var i = this.outputs.length - 1; i >= 0; i--) {
507 509 var out = this.outputs[i];
508 510 var output_type = out.output_type;
509 511 if (output_type == "display_data" && other) {
510 512 this.outputs.splice(i,1);
511 513 } else if (output_type == "stream") {
512 514 if (stdout && out.stream == "stdout") {
513 515 this.outputs.splice(i,1);
514 516 } else if (stderr && out.stream == "stderr") {
515 517 this.outputs.splice(i,1);
516 518 }
517 519 }
518 520 }
519 521 };
520 522
521 523
522 524 OutputArea.prototype.flush_clear_timeout = function() {
523 525 var output_div = this.element;
524 526 if (this.clear_out_timeout){
525 527 clearTimeout(this.clear_out_timeout);
526 528 this.clear_out_timeout = null;
527 529 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
528 530 };
529 531 }
530 532
531 533
532 534 // JSON serialization
533 535
534 536 OutputArea.prototype.fromJSON = function (outputs) {
535 537 var len = outputs.length;
536 538 for (var i=0; i<len; i++) {
537 539 // append with dynamic=false.
538 540 this.append_output(outputs[i], false);
539 541 };
540 542 };
541 543
542 544
543 545 OutputArea.prototype.toJSON = function () {
544 546 var outputs = [];
545 547 var len = this.outputs.length;
546 548 for (var i=0; i<len; i++) {
547 549 outputs[i] = this.outputs[i];
548 550 };
549 551 return outputs;
550 552 };
551 553
552 554
553 555 IPython.OutputArea = OutputArea;
554 556
555 557 return IPython;
556 558
557 559 }(IPython));
@@ -1,72 +1,73 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // QuickHelp button
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var QuickHelp = function (selector) {
15 15 };
16 16
17 17 QuickHelp.prototype.show_keyboard_shortcuts = function () {
18 18 // toggles display of keyboard shortcut dialog
19 19 var that = this;
20 20 if ( this.shortcut_dialog ){
21 21 // if dialog is already shown, close it
22 22 this.shortcut_dialog.dialog("close");
23 23 this.shortcut_dialog = null;
24 24 return;
25 25 }
26 26 var dialog = $('<div/>');
27 27 this.shortcut_dialog = dialog;
28 28 var shortcuts = [
29 29 {key: 'Shift-Enter', help: 'run cell'},
30 30 {key: 'Ctrl-Enter', help: 'run cell in-place'},
31 31 {key: 'Alt-Enter', help: 'run cell, insert below'},
32 32 {key: 'Ctrl-m x', help: 'cut cell'},
33 33 {key: 'Ctrl-m c', help: 'copy cell'},
34 34 {key: 'Ctrl-m v', help: 'paste cell'},
35 35 {key: 'Ctrl-m d', help: 'delete cell'},
36 {key: 'Ctrl-m z', help: 'undo last cell deletion'},
36 37 {key: 'Ctrl-m a', help: 'insert cell above'},
37 38 {key: 'Ctrl-m b', help: 'insert cell below'},
38 39 {key: 'Ctrl-m o', help: 'toggle output'},
39 40 {key: 'Ctrl-m O', help: 'toggle output scroll'},
40 41 {key: 'Ctrl-m l', help: 'toggle line numbers'},
41 42 {key: 'Ctrl-m s', help: 'save notebook'},
42 43 {key: 'Ctrl-m j', help: 'move cell down'},
43 44 {key: 'Ctrl-m k', help: 'move cell up'},
44 45 {key: 'Ctrl-m y', help: 'code cell'},
45 46 {key: 'Ctrl-m m', help: 'markdown cell'},
46 47 {key: 'Ctrl-m t', help: 'raw cell'},
47 48 {key: 'Ctrl-m 1-6', help: 'heading 1-6 cell'},
48 49 {key: 'Ctrl-m p', help: 'select previous'},
49 50 {key: 'Ctrl-m n', help: 'select next'},
50 51 {key: 'Ctrl-m i', help: 'interrupt kernel'},
51 52 {key: 'Ctrl-m .', help: 'restart kernel'},
52 53 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
53 54 ];
54 55 for (var i=0; i<shortcuts.length; i++) {
55 56 dialog.append($('<div>').
56 57 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
57 58 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
58 59 );
59 60 };
60 61 dialog.bind('dialogclose', function(event) {
61 62 // dialog has been closed, allow it to be drawn again.
62 63 that.shortcut_dialog = null;
63 64 });
64 65 dialog.dialog({title: 'Keyboard shortcuts', closeText: ''});
65 66 };
66 67
67 68 // Set module variables
68 69 IPython.QuickHelp = QuickHelp;
69 70
70 71 return IPython;
71 72
72 73 }(IPython));
@@ -1,421 +1,417 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2012 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // TextCell
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 // TextCell base class
15 15 var key = IPython.utils.keycodes;
16 16
17 17 var TextCell = function () {
18 18 this.code_mirror_mode = this.code_mirror_mode || 'htmlmixed';
19 19 IPython.Cell.apply(this, arguments);
20 20 this.rendered = false;
21 21 this.cell_type = this.cell_type || 'text';
22 22 };
23 23
24 24
25 25 TextCell.prototype = new IPython.Cell();
26 26
27 27
28 28 TextCell.prototype.create_element = function () {
29 29 var cell = $("<div>").addClass('cell text_cell border-box-sizing');
30 30 cell.attr('tabindex','2');
31 31 var input_area = $('<div/>').addClass('text_cell_input border-box-sizing');
32 32 this.code_mirror = CodeMirror(input_area.get(0), {
33 33 indentUnit : 4,
34 34 mode: this.code_mirror_mode,
35 35 theme: 'default',
36 36 value: this.placeholder,
37 37 readOnly: this.read_only,
38 38 lineWrapping : true,
39 39 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"},
40 40 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
41 41 });
42 42 // The tabindex=-1 makes this div focusable.
43 43 var render_area = $('<div/>').addClass('text_cell_render border-box-sizing').
44 44 addClass('rendered_html').attr('tabindex','-1');
45 45 cell.append(input_area).append(render_area);
46 46 this.element = cell;
47 47 };
48 48
49 49
50 50 TextCell.prototype.bind_events = function () {
51 51 IPython.Cell.prototype.bind_events.apply(this);
52 52 var that = this;
53 53 this.element.keydown(function (event) {
54 54 if (event.which === 13 && !event.shiftKey) {
55 55 if (that.rendered) {
56 56 that.edit();
57 57 return false;
58 58 };
59 59 };
60 60 });
61 61 this.element.dblclick(function () {
62 62 that.edit();
63 63 });
64 64 };
65 65
66 66
67 67 TextCell.prototype.handle_codemirror_keyevent = function (editor, event) {
68 68 // This method gets called in CodeMirror's onKeyDown/onKeyPress
69 69 // handlers and is used to provide custom key handling. Its return
70 70 // value is used to determine if CodeMirror should ignore the event:
71 71 // true = ignore, false = don't ignore.
72 72
73 73 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
74 74 // Always ignore shift-enter in CodeMirror as we handle it.
75 75 return true;
76 76 }
77 77 return false;
78 78 };
79 79
80 80
81 81 TextCell.prototype.select = function () {
82 82 IPython.Cell.prototype.select.apply(this);
83 83 var output = this.element.find("div.text_cell_render");
84 84 output.trigger('focus');
85 85 };
86 86
87 87
88 88 TextCell.prototype.unselect = function() {
89 89 // render on selection of another cell
90 90 this.render();
91 91 IPython.Cell.prototype.unselect.apply(this);
92 92 };
93 93
94 94
95 95 TextCell.prototype.edit = function () {
96 96 if ( this.read_only ) return;
97 97 if (this.rendered === true) {
98 98 var text_cell = this.element;
99 99 var output = text_cell.find("div.text_cell_render");
100 100 output.hide();
101 101 text_cell.find('div.text_cell_input').show();
102 102 this.code_mirror.refresh();
103 103 this.code_mirror.focus();
104 104 // We used to need an additional refresh() after the focus, but
105 105 // it appears that this has been fixed in CM. This bug would show
106 106 // up on FF when a newly loaded markdown cell was edited.
107 107 this.rendered = false;
108 108 if (this.get_text() === this.placeholder) {
109 109 this.set_text('');
110 110 this.refresh();
111 111 }
112 112 }
113 113 };
114 114
115 115
116 116 // Subclasses must define render.
117 117 TextCell.prototype.render = function () {};
118 118
119 119
120 120 TextCell.prototype.get_text = function() {
121 121 return this.code_mirror.getValue();
122 122 };
123 123
124 124
125 125 TextCell.prototype.set_text = function(text) {
126 126 this.code_mirror.setValue(text);
127 127 this.code_mirror.refresh();
128 128 };
129 129
130 130
131 131 TextCell.prototype.get_rendered = function() {
132 132 return this.element.find('div.text_cell_render').html();
133 133 };
134 134
135 135
136 136 TextCell.prototype.set_rendered = function(text) {
137 137 this.element.find('div.text_cell_render').html(text);
138 138 };
139 139
140 140
141 141 TextCell.prototype.at_top = function () {
142 142 if (this.rendered) {
143 143 return true;
144 144 } else {
145 145 return false;
146 146 }
147 147 };
148 148
149 149
150 150 TextCell.prototype.at_bottom = function () {
151 151 if (this.rendered) {
152 152 return true;
153 153 } else {
154 154 return false;
155 155 }
156 156 };
157 157
158 158
159 159 TextCell.prototype.fromJSON = function (data) {
160 160 IPython.Cell.prototype.fromJSON.apply(this, arguments);
161 161 if (data.cell_type === this.cell_type) {
162 162 if (data.source !== undefined) {
163 163 this.set_text(data.source);
164 164 // make this value the starting point, so that we can only undo
165 165 // to this state, instead of a blank cell
166 166 this.code_mirror.clearHistory();
167 167 this.set_rendered(data.rendered || '');
168 168 this.rendered = false;
169 169 this.render();
170 170 }
171 171 }
172 172 };
173 173
174 174
175 175 TextCell.prototype.toJSON = function () {
176 176 var data = IPython.Cell.prototype.toJSON.apply(this);
177 177 data.cell_type = this.cell_type;
178 178 data.source = this.get_text();
179 179 return data;
180 180 };
181 181
182 182
183 183 // HTMLCell
184 184
185 185 var HTMLCell = function () {
186 186 this.placeholder = "Type <strong>HTML</strong> and LaTeX: $\\alpha^2$";
187 187 IPython.TextCell.apply(this, arguments);
188 188 this.cell_type = 'html';
189 189 };
190 190
191 191
192 192 HTMLCell.prototype = new TextCell();
193 193
194 194
195 195 HTMLCell.prototype.render = function () {
196 196 if (this.rendered === false) {
197 197 var text = this.get_text();
198 198 if (text === "") { text = this.placeholder; }
199 199 this.set_rendered(text);
200 200 this.typeset();
201 201 this.element.find('div.text_cell_input').hide();
202 202 this.element.find("div.text_cell_render").show();
203 203 this.rendered = true;
204 204 }
205 205 };
206 206
207 207
208 208 // MarkdownCell
209 209
210 210 var MarkdownCell = function () {
211 211 this.placeholder = "Type *Markdown* and LaTeX: $\\alpha^2$";
212 212 IPython.TextCell.apply(this, arguments);
213 213 this.cell_type = 'markdown';
214 214 };
215 215
216 216
217 217 MarkdownCell.prototype = new TextCell();
218 218
219 219
220 220 MarkdownCell.prototype.render = function () {
221 221 if (this.rendered === false) {
222 222 var text = this.get_text();
223 223 if (text === "") { text = this.placeholder; }
224
225 224 text = IPython.mathjaxutils.remove_math(text)
226 225 var html = IPython.markdown_converter.makeHtml(text);
227 226 html = IPython.mathjaxutils.replace_math(html)
228
229 227 try {
230 228 this.set_rendered(html);
231 229 } catch (e) {
232 230 console.log("Error running Javascript in Markdown:");
233 231 console.log(e);
234 232 this.set_rendered($("<div/>").addClass("js-error").html(
235 233 "Error rendering Markdown!<br/>" + e.toString())
236 234 );
237 235 }
238 this.typeset()
239 236 this.element.find('div.text_cell_input').hide();
240 237 this.element.find("div.text_cell_render").show();
241 238 var code_snippets = this.element.find("pre > code");
242 239 code_snippets.replaceWith(function () {
243 240 var code = $(this).html();
244 241 /* Substitute br for newlines and &nbsp; for spaces
245 242 before highlighting, since prettify doesn't
246 243 preserve those on all browsers */
247 244 code = code.replace(/(\r\n|\n|\r)/gm, "<br/>");
248 245 code = code.replace(/ /gm, '&nbsp;');
249 246 code = prettyPrintOne(code);
250 247
251 248 return '<code class="prettyprint">' + code + '</code>';
252 249 });
253
254 IPython.mathjaxutils.queue_render()
250 this.typeset()
255 251 this.rendered = true;
256 252 }
257 253 };
258 254
259 255
260 256 // RawCell
261 257
262 258 var RawCell = function () {
263 259 this.placeholder = "Type plain text and LaTeX: $\\alpha^2$";
264 260 this.code_mirror_mode = 'rst';
265 261 IPython.TextCell.apply(this, arguments);
266 262 this.cell_type = 'raw';
267 263 var that = this
268 264
269 265 this.element.focusout(
270 266 function() { that.auto_highlight(); }
271 267 );
272 268 };
273 269
274 270
275 271 RawCell.prototype = new TextCell();
276 272
277 273 RawCell.prototype.auto_highlight = function () {
278 274 this._auto_highlight(IPython.config.raw_cell_highlight);
279 275 };
280 276
281 277 RawCell.prototype.render = function () {
282 278 this.rendered = true;
283 279 this.edit();
284 280 };
285 281
286 282
287 283 RawCell.prototype.handle_codemirror_keyevent = function (editor, event) {
288 284 // This method gets called in CodeMirror's onKeyDown/onKeyPress
289 285 // handlers and is used to provide custom key handling. Its return
290 286 // value is used to determine if CodeMirror should ignore the event:
291 287 // true = ignore, false = don't ignore.
292 288
293 289 var that = this;
294 290 if (event.which === key.UPARROW && event.type === 'keydown') {
295 291 // If we are not at the top, let CM handle the up arrow and
296 292 // prevent the global keydown handler from handling it.
297 293 if (!that.at_top()) {
298 294 event.stop();
299 295 return false;
300 296 } else {
301 297 return true;
302 298 };
303 299 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
304 300 // If we are not at the bottom, let CM handle the down arrow and
305 301 // prevent the global keydown handler from handling it.
306 302 if (!that.at_bottom()) {
307 303 event.stop();
308 304 return false;
309 305 } else {
310 306 return true;
311 307 };
312 308 };
313 309 return false;
314 310 };
315 311
316 312
317 313 RawCell.prototype.select = function () {
318 314 IPython.Cell.prototype.select.apply(this);
319 315 this.code_mirror.refresh();
320 316 this.code_mirror.focus();
321 317 };
322 318
323 319
324 320 RawCell.prototype.at_top = function () {
325 321 var cursor = this.code_mirror.getCursor();
326 322 if (cursor.line === 0 && cursor.ch === 0) {
327 323 return true;
328 324 } else {
329 325 return false;
330 326 }
331 327 };
332 328
333 329
334 330 RawCell.prototype.at_bottom = function () {
335 331 var cursor = this.code_mirror.getCursor();
336 332 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
337 333 return true;
338 334 } else {
339 335 return false;
340 336 }
341 337 };
342 338
343 339
344 340 // HTMLCell
345 341
346 342 var HeadingCell = function () {
347 343 this.placeholder = "Type Heading Here";
348 344 IPython.TextCell.apply(this, arguments);
349 345 this.cell_type = 'heading';
350 346 this.level = 1;
351 347 };
352 348
353 349
354 350 HeadingCell.prototype = new TextCell();
355 351
356 352
357 353 HeadingCell.prototype.fromJSON = function (data) {
358 354 if (data.level != undefined){
359 355 this.level = data.level;
360 356 }
361 357 IPython.TextCell.prototype.fromJSON.apply(this, arguments);
362 358 };
363 359
364 360
365 361 HeadingCell.prototype.toJSON = function () {
366 362 var data = IPython.TextCell.prototype.toJSON.apply(this);
367 363 data.level = this.get_level();
368 364 return data;
369 365 };
370 366
371 367
372 368 HeadingCell.prototype.set_level = function (level) {
373 369 this.level = level;
374 370 if (this.rendered) {
375 371 this.rendered = false;
376 372 this.render();
377 373 };
378 374 };
379 375
380 376
381 377 HeadingCell.prototype.get_level = function () {
382 378 return this.level;
383 379 };
384 380
385 381
386 382 HeadingCell.prototype.set_rendered = function (text) {
387 383 var r = this.element.find("div.text_cell_render");
388 384 r.empty();
389 385 r.append($('<h'+this.level+'/>').html(text));
390 386 };
391 387
392 388
393 389 HeadingCell.prototype.get_rendered = function () {
394 390 var r = this.element.find("div.text_cell_render");
395 391 return r.children().first().html();
396 392 };
397 393
398 394
399 395 HeadingCell.prototype.render = function () {
400 396 if (this.rendered === false) {
401 397 var text = this.get_text();
402 398 if (text === "") { text = this.placeholder; }
403 399 this.set_rendered(text);
404 400 this.typeset();
405 401 this.element.find('div.text_cell_input').hide();
406 402 this.element.find("div.text_cell_render").show();
407 403 this.rendered = true;
408 404 };
409 405 };
410 406
411 407 IPython.TextCell = TextCell;
412 408 IPython.HTMLCell = HTMLCell;
413 409 IPython.MarkdownCell = MarkdownCell;
414 410 IPython.RawCell = RawCell;
415 411 IPython.HeadingCell = HeadingCell;
416 412
417 413
418 414 return IPython;
419 415
420 416 }(IPython));
421 417
@@ -1,267 +1,288 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2012 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Utilities
10 10 //============================================================================
11 11
12 12 IPython.namespace('IPython.utils');
13 13
14 14 IPython.utils = (function (IPython) {
15 15
16 16 //============================================================================
17 17 // Cross-browser RegEx Split
18 18 //============================================================================
19 19
20 20 // This code has been MODIFIED from the code licensed below to not replace the
21 21 // default browser split. The license is reproduced here.
22 22
23 23 // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
24 24 /*!
25 25 * Cross-Browser Split 1.1.1
26 26 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
27 27 * Available under the MIT License
28 28 * ECMAScript compliant, uniform cross-browser split method
29 29 */
30 30
31 31 /**
32 32 * Splits a string into an array of strings using a regex or string
33 33 * separator. Matches of the separator are not included in the result array.
34 34 * However, if `separator` is a regex that contains capturing groups,
35 35 * backreferences are spliced into the result each time `separator` is
36 36 * matched. Fixes browser bugs compared to the native
37 37 * `String.prototype.split` and can be used reliably cross-browser.
38 38 * @param {String} str String to split.
39 39 * @param {RegExp|String} separator Regex or string to use for separating
40 40 * the string.
41 41 * @param {Number} [limit] Maximum number of items to include in the result
42 42 * array.
43 43 * @returns {Array} Array of substrings.
44 44 * @example
45 45 *
46 46 * // Basic use
47 47 * regex_split('a b c d', ' ');
48 48 * // -> ['a', 'b', 'c', 'd']
49 49 *
50 50 * // With limit
51 51 * regex_split('a b c d', ' ', 2);
52 52 * // -> ['a', 'b']
53 53 *
54 54 * // Backreferences in result array
55 55 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
56 56 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
57 57 */
58 58 var regex_split = function (str, separator, limit) {
59 59 // If `separator` is not a regex, use `split`
60 60 if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
61 61 return split.call(str, separator, limit);
62 62 }
63 63 var output = [],
64 64 flags = (separator.ignoreCase ? "i" : "") +
65 65 (separator.multiline ? "m" : "") +
66 66 (separator.extended ? "x" : "") + // Proposed for ES6
67 67 (separator.sticky ? "y" : ""), // Firefox 3+
68 68 lastLastIndex = 0,
69 69 // Make `global` and avoid `lastIndex` issues by working with a copy
70 70 separator = new RegExp(separator.source, flags + "g"),
71 71 separator2, match, lastIndex, lastLength;
72 72 str += ""; // Type-convert
73 73
74 74 compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined"
75 75 if (!compliantExecNpcg) {
76 76 // Doesn't need flags gy, but they don't hurt
77 77 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
78 78 }
79 79 /* Values for `limit`, per the spec:
80 80 * If undefined: 4294967295 // Math.pow(2, 32) - 1
81 81 * If 0, Infinity, or NaN: 0
82 82 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
83 83 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
84 84 * If other: Type-convert, then use the above rules
85 85 */
86 86 limit = typeof(limit) === "undefined" ?
87 87 -1 >>> 0 : // Math.pow(2, 32) - 1
88 88 limit >>> 0; // ToUint32(limit)
89 89 while (match = separator.exec(str)) {
90 90 // `separator.lastIndex` is not reliable cross-browser
91 91 lastIndex = match.index + match[0].length;
92 92 if (lastIndex > lastLastIndex) {
93 93 output.push(str.slice(lastLastIndex, match.index));
94 94 // Fix browsers whose `exec` methods don't consistently return `undefined` for
95 95 // nonparticipating capturing groups
96 96 if (!compliantExecNpcg && match.length > 1) {
97 97 match[0].replace(separator2, function () {
98 98 for (var i = 1; i < arguments.length - 2; i++) {
99 99 if (typeof(arguments[i]) === "undefined") {
100 100 match[i] = undefined;
101 101 }
102 102 }
103 103 });
104 104 }
105 105 if (match.length > 1 && match.index < str.length) {
106 106 Array.prototype.push.apply(output, match.slice(1));
107 107 }
108 108 lastLength = match[0].length;
109 109 lastLastIndex = lastIndex;
110 110 if (output.length >= limit) {
111 111 break;
112 112 }
113 113 }
114 114 if (separator.lastIndex === match.index) {
115 115 separator.lastIndex++; // Avoid an infinite loop
116 116 }
117 117 }
118 118 if (lastLastIndex === str.length) {
119 119 if (lastLength || !separator.test("")) {
120 120 output.push("");
121 121 }
122 122 } else {
123 123 output.push(str.slice(lastLastIndex));
124 124 }
125 125 return output.length > limit ? output.slice(0, limit) : output;
126 126 };
127 127
128 128 //============================================================================
129 129 // End contributed Cross-browser RegEx Split
130 130 //============================================================================
131 131
132 132
133 133 var uuid = function () {
134 134 // http://www.ietf.org/rfc/rfc4122.txt
135 135 var s = [];
136 136 var hexDigits = "0123456789ABCDEF";
137 137 for (var i = 0; i < 32; i++) {
138 138 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
139 139 }
140 140 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
141 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 143 var uuid = s.join("");
144 144 return uuid;
145 145 };
146 146
147 147
148 148 //Fix raw text to parse correctly in crazy XML
149 149 function xmlencode(string) {
150 150 return string.replace(/\&/g,'&'+'amp;')
151 151 .replace(/</g,'&'+'lt;')
152 152 .replace(/>/g,'&'+'gt;')
153 153 .replace(/\'/g,'&'+'apos;')
154 154 .replace(/\"/g,'&'+'quot;')
155 155 .replace(/`/g,'&'+'#96;');
156 156 }
157 157
158 158
159 159 //Map from terminal commands to CSS classes
160 160 ansi_colormap = {
161 161 "30":"ansiblack", "31":"ansired",
162 162 "32":"ansigreen", "33":"ansiyellow",
163 163 "34":"ansiblue", "35":"ansipurple","36":"ansicyan",
164 164 "37":"ansigrey", "01":"ansibold"
165 165 };
166 166
167 167 // Transform ANSI color escape codes into HTML <span> tags with css
168 168 // classes listed in the above ansi_colormap object. The actual color used
169 169 // are set in the css file.
170 170 function fixConsole(txt) {
171 171 txt = xmlencode(txt);
172 172 var re = /\033\[([\dA-Fa-f;]*?)m/;
173 173 var opened = false;
174 174 var cmds = [];
175 175 var opener = "";
176 176 var closer = "";
177 177 while (re.test(txt)) {
178 178 var cmds = txt.match(re)[1].split(";");
179 179 closer = opened?"</span>":"";
180 180 opened = cmds.length > 1 || cmds[0] != 0;
181 181 var rep = [];
182 182 for (var i in cmds)
183 183 if (typeof(ansi_colormap[cmds[i]]) != "undefined")
184 184 rep.push(ansi_colormap[cmds[i]]);
185 185 opener = rep.length > 0?"<span class=\""+rep.join(" ")+"\">":"";
186 186 txt = txt.replace(re, closer + opener);
187 187 }
188 188 if (opened) txt += "</span>";
189 189 return txt;
190 190 }
191 191
192 192 // Remove chunks that should be overridden by the effect of
193 193 // carriage return characters
194 194 function fixCarriageReturn(txt) {
195 195 tmp = txt;
196 196 do {
197 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 200 } while (tmp.length < txt.length);
200 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 222 grow = function(element) {
204 223 // Grow the cell by hand. This is used upon reloading from JSON, when the
205 224 // autogrow handler is not called.
206 225 var dom = element.get(0);
207 226 var lines_count = 0;
208 227 // modified split rule from
209 228 // http://stackoverflow.com/questions/2035910/how-to-get-the-number-of-lines-in-a-textarea/2036424#2036424
210 229 var lines = dom.value.split(/\r|\r\n|\n/);
211 230 lines_count = lines.length;
212 231 if (lines_count >= 1) {
213 232 dom.rows = lines_count;
214 233 } else {
215 234 dom.rows = 1;
216 235 }
217 236 };
218 237
219 238 // some keycodes that seem to be platform/browser independant
220 239 var keycodes ={
221 240 BACKSPACE: 8,
222 241 TAB : 9,
223 242 ENTER : 13,
224 243 SHIFT : 16,
225 244 CTRL : 17,
226 245 CONTROL : 17,
227 246 ALT : 18,
228 247 ESC : 27,
229 248 SPACE : 32,
230 249 PGUP : 33,
231 250 PGDOWN : 34,
232 251 LEFT_ARROW: 37,
233 252 LEFTARROW: 37,
234 253 LEFT : 37,
235 254 UP_ARROW : 38,
236 255 UPARROW : 38,
237 256 UP : 38,
238 257 RIGHT_ARROW:39,
239 258 RIGHTARROW:39,
240 259 RIGHT : 39,
241 260 DOWN_ARROW: 40,
242 261 DOWNARROW: 40,
243 262 DOWN : 40,
244 263 };
245 264
246 265
247 266 points_to_pixels = function (points) {
248 267 // A reasonably good way of converting between points and pixels.
249 268 var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
250 269 $(body).append(test);
251 270 var pixel_per_point = test.width()/10000;
252 271 test.remove();
253 272 return Math.floor(points*pixel_per_point);
254 273 }
255 274
256 275
257 276 return {
258 277 regex_split : regex_split,
259 278 uuid : uuid,
260 279 fixConsole : fixConsole,
261 280 keycodes : keycodes,
262 281 grow : grow,
263 282 fixCarriageReturn : fixCarriageReturn,
283 wrapUrls : wrapUrls,
284 autoLinkUrls : autoLinkUrls,
264 285 points_to_pixels : points_to_pixels
265 286 };
266 287
267 288 }(IPython));
@@ -1,224 +1,226 b''
1 1 {% extends page.html %}
2 2 {% block stylesheet %}
3 3
4 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 6 {% end %}
7 7 <script type="text/javascript">
8 8 // MathJax disabled, set as null to distingish from *missing* MathJax,
9 9 // where it will be undefined, and should prompt a dialog later.
10 10 window.mathjax_url = "{{mathjax_url}}";
11 11 </script>
12 12
13 13 <link rel="stylesheet" href="{{ static_url("codemirror/lib/codemirror.css") }}">
14 14 <link rel="stylesheet" href="{{ static_url("codemirror/theme/ipython.css") }}">
15 15
16 16 <link rel="stylesheet" href="{{ static_url("prettify/prettify.css") }}"/>
17 17
18 18 <link rel="stylesheet" href="{{ static_url("css/notebook.css") }}" type="text/css" />
19 19 <link rel="stylesheet" href="{{ static_url("css/tooltip.css") }}" type="text/css" />
20 20 <link rel="stylesheet" href="{{ static_url("css/renderedhtml.css") }}" type="text/css" />
21 21
22 22 <link rel="stylesheet" href="{{ static_url("css/printnotebook.css") }}" type="text/css" media="print"/>
23 23
24 24 {% end %}
25 25
26 26
27 27 {% block params %}
28 28
29 29 data-project={{project}}
30 30 data-base-project-url={{base_project_url}}
31 31 data-base-kernel-url={{base_kernel_url}}
32 32 data-read-only={{read_only and not logged_in}}
33 33 data-notebook-id={{notebook_id}}
34 34
35 35 {% end %}
36 36
37 37
38 38 {% block header %}
39 39
40 40 <span id="save_widget">
41 41 <span id="notebook_name"></span>
42 42 <span id="save_status"></span>
43 43 </span>
44 44
45 45 {% end %}
46 46
47 47
48 48 {% block site %}
49 49
50 50 <div id="menubar_container">
51 51 <div id="menubar">
52 52 <ul id="menus">
53 53 <li><a href="#">File</a>
54 54 <ul>
55 55 <li id="new_notebook"><a href="#">New</a></li>
56 56 <li id="open_notebook"><a href="#">Open...</a></li>
57 57 <hr/>
58 58 <li id="copy_notebook"><a href="#">Make a Copy...</a></li>
59 59 <li id="rename_notebook"><a href="#">Rename...</a></li>
60 60 <li id="save_notebook"><a href="#">Save</a></li>
61 61 <hr/>
62 62 <li><a href="#">Download as</a>
63 63 <ul>
64 64 <li id="download_ipynb"><a href="#">IPython (.ipynb)</a></li>
65 65 <li id="download_py"><a href="#">Python (.py)</a></li>
66 66 </ul>
67 67 </li>
68 68 <hr/>
69 69 <li id="print_notebook"><a href="/{{notebook_id}}/print" target="_blank">Print View</a></li>
70 70 <hr/>
71 71 <li id="kill_and_exit"><a href="#" >Close and halt</a></li>
72 72 </ul>
73 73 </li>
74 74 <li><a href="#">Edit</a>
75 75 <ul>
76 76 <li id="cut_cell"><a href="#">Cut Cell</a></li>
77 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 78 <li id="paste_cell_above" class="ui-state-disabled"><a href="#">Paste Cell Above</a></li>
80 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 81 <li id="delete_cell"><a href="#">Delete</a></li>
82 82 <hr/>
83 83 <li id="split_cell"><a href="#">Split Cell</a></li>
84 84 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
85 85 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
86 86 <hr/>
87 87 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
88 88 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
89 89 <hr/>
90 90 <li id="select_previous"><a href="#">Select Previous Cell</a></li>
91 91 <li id="select_next"><a href="#">Select Next Cell</a></li>
92 92 </ul>
93 93 </li>
94 94 <li><a href="#">View</a>
95 95 <ul>
96 96 <li id="toggle_header"><a href="#">Toggle Header</a></li>
97 97 <li id="toggle_toolbar"><a href="#">Toggle Toolbar</a></li>
98 98 </ul>
99 99 </li>
100 100 <li><a href="#">Insert</a>
101 101 <ul>
102 102 <li id="insert_cell_above"><a href="#">Insert Cell Above</a></li>
103 103 <li id="insert_cell_below"><a href="#">Insert Cell Below</a></li>
104 104 </ul>
105 105 </li>
106 106 <li><a href="#">Cell</a>
107 107 <ul>
108 108 <li id="run_cell"><a href="#">Run</a></li>
109 109 <li id="run_cell_in_place"><a href="#">Run in Place</a></li>
110 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 113 <hr/>
112 114 <li id="to_code"><a href="#">Code</a></li>
113 115 <li id="to_markdown"><a href="#">Markdown </a></li>
114 116 <li id="to_raw"><a href="#">Raw Text</a></li>
115 117 <li id="to_heading1"><a href="#">Heading 1</a></li>
116 118 <li id="to_heading2"><a href="#">Heading 2</a></li>
117 119 <li id="to_heading3"><a href="#">Heading 3</a></li>
118 120 <li id="to_heading4"><a href="#">Heading 4</a></li>
119 121 <li id="to_heading5"><a href="#">Heading 5</a></li>
120 122 <li id="to_heading6"><a href="#">Heading 6</a></li>
121 123 <hr/>
122 124 <li id="toggle_output"><a href="#">Toggle Current Output</a></li>
123 125 <li id="all_outputs"><a href="#">All Output</a>
124 126 <ul>
125 127 <li id="expand_all_output"><a href="#">Expand</a></li>
126 128 <li id="scroll_all_output"><a href="#">Scroll Long</a></li>
127 129 <li id="collapse_all_output"><a href="#">Collapse</a></li>
128 130 <li id="clear_all_output"><a href="#">Clear</a></li>
129 131 </ul>
130 132 </li>
131 133 </ul>
132 134 </li>
133 135 <li><a href="#">Kernel</a>
134 136 <ul>
135 137 <li id="int_kernel"><a href="#">Interrupt</a></li>
136 138 <li id="restart_kernel"><a href="#">Restart</a></li>
137 139 </ul>
138 140 </li>
139 141 <li><a href="#">Help</a>
140 142 <ul>
141 143 <li><a href="http://ipython.org/documentation.html" target="_blank">IPython Help</a></li>
142 144 <li><a href="http://ipython.org/ipython-doc/stable/interactive/htmlnotebook.html" target="_blank">Notebook Help</a></li>
143 145 <li id="keyboard_shortcuts"><a href="#">Keyboard Shortcuts</a></li>
144 146 <hr/>
145 147 <li><a href="http://docs.python.org" target="_blank">Python</a></li>
146 148 <li><a href="http://docs.scipy.org/doc/numpy/reference/" target="_blank">NumPy</a></li>
147 149 <li><a href="http://docs.scipy.org/doc/scipy/reference/" target="_blank">SciPy</a></li>
148 150 <li><a href="http://docs.sympy.org/dev/index.html" target="_blank">SymPy</a></li>
149 151 <li><a href="http://matplotlib.sourceforge.net/" target="_blank">Matplotlib</a></li>
150 152 </ul>
151 153 </li>
152 154 </ul>
153 155
154 156 </div>
155 157 <div id="notification_area">
156 158 </div>
157 159 </div>
158 160
159 161
160 162 <div id="maintoolbar"></div>
161 163
162 164 <div id="main_app">
163 165
164 166 <div id="notebook_panel">
165 167 <div id="notebook"></div>
166 168 <div id="pager_splitter"></div>
167 169 <div id="pager_container">
168 170 <div id='pager_button_area'>
169 171 </div>
170 172 <div id="pager"></div>
171 173 </div>
172 174 </div>
173 175
174 176 </div>
175 177 <div id='tooltip' class='ipython_tooltip ui-corner-all' style='display:none'></div>
176 178
177 179
178 180 {% end %}
179 181
180 182
181 183 {% block script %}
182 184
183 185 <script src="{{ static_url("codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
184 186 <script src="{{ static_url("codemirror/lib/util/loadmode.js") }}" charset="utf-8"></script>
185 187 <script src="{{ static_url("codemirror/lib/util/multiplex.js") }}" charset="utf-8"></script>
186 188 <script src="{{ static_url("codemirror/mode/python/python.js") }}" charset="utf-8"></script>
187 189 <script src="{{ static_url("codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
188 190 <script src="{{ static_url("codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
189 191 <script src="{{ static_url("codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
190 192 <script src="{{ static_url("codemirror/mode/css/css.js") }}" charset="utf-8"></script>
191 193 <script src="{{ static_url("codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
192 194 <script src="{{ static_url("codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
193 195
194 196 <script src="{{ static_url("pagedown/Markdown.Converter.js") }}" charset="utf-8"></script>
195 197
196 198 <script src="{{ static_url("prettify/prettify.js") }}" charset="utf-8"></script>
197 199 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
198 200
199 201 <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script>
200 202 <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
201 203 <script src="{{ static_url("js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script>
202 204 <script src="{{ static_url("js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
203 205 <script src="{{ static_url("js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
204 206 <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
205 207 <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
206 208 <script src="{{ static_url("js/completer.js") }}" type="text/javascript" charset="utf-8"></script>
207 209 <script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
208 210 <script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
209 211 <script src="{{ static_url("js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
210 212 <script src="{{ static_url("js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
211 213 <script src="{{ static_url("js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
212 214 <script src="{{ static_url("js/menubar.js") }}" type="text/javascript" charset="utf-8"></script>
213 215 <script src="{{ static_url("js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
214 216 <script src="{{ static_url("js/maintoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
215 217 <script src="{{ static_url("js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
216 218 <script src="{{ static_url("js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
217 219 <script src="{{ static_url("js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script>
218 220 <script src="{{ static_url("js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script>
219 221 <script src="{{ static_url("js/config.js") }}" type="text/javascript" charset="utf-8"></script>
220 222 <script src="{{ static_url("js/notebookmain.js") }}" type="text/javascript" charset="utf-8"></script>
221 223
222 224 <script src="{{ static_url("js/contexthint.js") }}" charset="utf-8"></script>
223 225
224 226 {% end %}
@@ -1,81 +1,81 b''
1 1 {% extends page.html %}
2 2
3 3 {% block stylesheet %}
4 4
5 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 7 {% end %}
8 8 <script type="text/javascript">
9 9 // MathJax disabled, set as null to distingish from *missing* MathJax,
10 10 // where it will be undefined, and should prompt a dialog later.
11 11 window.mathjax_url = "{{mathjax_url}}";
12 12 </script>
13 13
14 14 <link rel="stylesheet" href="{{ static_url("codemirror/lib/codemirror.css") }}">
15 15 <link rel="stylesheet" href="{{ static_url("codemirror/theme/ipython.css") }}">
16 16
17 17 <link rel="stylesheet" href="{{ static_url("prettify/prettify.css") }}"/>
18 18
19 19 <link rel="stylesheet" href="{{ static_url("css/notebook.css") }}" type="text/css" />
20 20 <link rel="stylesheet" href="{{ static_url("css/printnotebook.css") }}" type="text/css" />
21 21 <link rel="stylesheet" href="{{ static_url("css/renderedhtml.css") }}" type="text/css" />
22 22
23 23 {% end %}
24 24
25 25
26 26 {% block params %}
27 27
28 28 data-project={{project}}
29 29 data-base-project-url={{base_project_url}}
30 30 data-base-kernel-url={{base_kernel_url}}
31 31 data-read-only={{read_only and not logged_in}}
32 32 data-notebook-id={{notebook_id}}
33 33
34 34 {% end %}
35 35
36 36
37 37 {% block header %}
38 38 {% end %}
39 39
40 40
41 41 {% block site %}
42 42
43 43 <div id="main_app">
44 44
45 45 <div id="notebook_panel">
46 46 <div id="notebook"></div>
47 47 </div>
48 48
49 49 </div>
50 50
51 51 {% end %}
52 52
53 53
54 54 {% block script %}
55 55
56 56 <script src="{{ static_url("codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
57 57 <script src="{{ static_url("codemirror/mode/python/python.js") }}" charset="utf-8"></script>
58 58 <script src="{{ static_url("codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
59 59 <script src="{{ static_url("codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
60 60 <script src="{{ static_url("codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
61 61 <script src="{{ static_url("codemirror/mode/css/css.js") }}" charset="utf-8"></script>
62 62 <script src="{{ static_url("codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
63 63 <script src="{{ static_url("codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
64 64
65 65 <script src="{{ static_url("pagedown/Markdown.Converter.js") }}" charset="utf-8"></script>
66 66
67 67 <script src="{{ static_url("prettify/prettify.js") }}" charset="utf-8"></script>
68 68 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
69 69
70 70 <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script>
71 71 <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
72 72 <script src="{{ static_url("js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
73 73 <script src="{{ static_url("js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
74 74 <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
75 75 <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
76 76 <script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
77 77 <script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
78 78 <script src="{{ static_url("js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
79 79 <script src="{{ static_url("js/printnotebookmain.js") }}" type="text/javascript" charset="utf-8"></script>
80 80
81 81 {% end %}
@@ -1,1959 +1,1999 b''
1 1 """ An abstract base class for console-type widgets.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Imports
5 5 #-----------------------------------------------------------------------------
6 6
7 7 # Standard library imports
8 8 import os.path
9 9 import re
10 10 import sys
11 11 from textwrap import dedent
12 12 from unicodedata import category
13 13 import webbrowser
14 14
15 15 # System library imports
16 16 from IPython.external.qt import QtCore, QtGui
17 17
18 18 # Local imports
19 19 from IPython.config.configurable import LoggingConfigurable
20 20 from IPython.core.inputsplitter import ESC_SEQUENCES
21 21 from IPython.frontend.qt.rich_text import HtmlExporter
22 22 from IPython.frontend.qt.util import MetaQObjectHasTraits, get_font
23 23 from IPython.utils.text import columnize
24 24 from IPython.utils.traitlets import Bool, Enum, Integer, Unicode
25 25 from ansi_code_processor import QtAnsiCodeProcessor
26 26 from completion_widget import CompletionWidget
27 27 from completion_html import CompletionHtml
28 28 from completion_plain import CompletionPlain
29 29 from kill_ring import QtKillRing
30 30
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # Functions
34 34 #-----------------------------------------------------------------------------
35 35
36 36 ESCAPE_CHARS = ''.join(ESC_SEQUENCES)
37 37 ESCAPE_RE = re.compile("^["+ESCAPE_CHARS+"]+")
38 38
39 39 def commonprefix(items):
40 40 """Get common prefix for completions
41 41
42 42 Return the longest common prefix of a list of strings, but with special
43 43 treatment of escape characters that might precede commands in IPython,
44 44 such as %magic functions. Used in tab completion.
45 45
46 46 For a more general function, see os.path.commonprefix
47 47 """
48 48 # the last item will always have the least leading % symbol
49 49 # min / max are first/last in alphabetical order
50 50 first_match = ESCAPE_RE.match(min(items))
51 51 last_match = ESCAPE_RE.match(max(items))
52 52 # common suffix is (common prefix of reversed items) reversed
53 53 if first_match and last_match:
54 54 prefix = os.path.commonprefix((first_match.group(0)[::-1], last_match.group(0)[::-1]))[::-1]
55 55 else:
56 56 prefix = ''
57 57
58 58 items = [s.lstrip(ESCAPE_CHARS) for s in items]
59 59 return prefix+os.path.commonprefix(items)
60 60
61 61 def is_letter_or_number(char):
62 62 """ Returns whether the specified unicode character is a letter or a number.
63 63 """
64 64 cat = category(char)
65 65 return cat.startswith('L') or cat.startswith('N')
66 66
67 67 #-----------------------------------------------------------------------------
68 68 # Classes
69 69 #-----------------------------------------------------------------------------
70 70
71 71 class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):
72 72 """ An abstract base class for console-type widgets. This class has
73 73 functionality for:
74 74
75 75 * Maintaining a prompt and editing region
76 76 * Providing the traditional Unix-style console keyboard shortcuts
77 77 * Performing tab completion
78 78 * Paging text
79 79 * Handling ANSI escape codes
80 80
81 81 ConsoleWidget also provides a number of utility methods that will be
82 82 convenient to implementors of a console-style widget.
83 83 """
84 84 __metaclass__ = MetaQObjectHasTraits
85 85
86 86 #------ Configuration ------------------------------------------------------
87 87
88 88 ansi_codes = Bool(True, config=True,
89 89 help="Whether to process ANSI escape codes."
90 90 )
91 91 buffer_size = Integer(500, config=True,
92 92 help="""
93 93 The maximum number of lines of text before truncation. Specifying a
94 94 non-positive number disables text truncation (not recommended).
95 95 """
96 96 )
97 97 gui_completion = Enum(['plain', 'droplist', 'ncurses'], config=True,
98 98 default_value = 'ncurses',
99 99 help="""
100 100 The type of completer to use. Valid values are:
101 101
102 102 'plain' : Show the availlable completion as a text list
103 103 Below the editting area.
104 104 'droplist': Show the completion in a drop down list navigable
105 105 by the arrow keys, and from which you can select
106 106 completion by pressing Return.
107 107 'ncurses' : Show the completion as a text list which is navigable by
108 108 `tab` and arrow keys.
109 109 """
110 110 )
111 111 # NOTE: this value can only be specified during initialization.
112 112 kind = Enum(['plain', 'rich'], default_value='plain', config=True,
113 113 help="""
114 114 The type of underlying text widget to use. Valid values are 'plain',
115 115 which specifies a QPlainTextEdit, and 'rich', which specifies a
116 116 QTextEdit.
117 117 """
118 118 )
119 119 # NOTE: this value can only be specified during initialization.
120 120 paging = Enum(['inside', 'hsplit', 'vsplit', 'custom', 'none'],
121 121 default_value='inside', config=True,
122 122 help="""
123 123 The type of paging to use. Valid values are:
124 124
125 125 'inside' : The widget pages like a traditional terminal.
126 126 'hsplit' : When paging is requested, the widget is split
127 127 horizontally. The top pane contains the console, and the
128 128 bottom pane contains the paged text.
129 129 'vsplit' : Similar to 'hsplit', except that a vertical splitter
130 130 used.
131 131 'custom' : No action is taken by the widget beyond emitting a
132 132 'custom_page_requested(str)' signal.
133 133 'none' : The text is written directly to the console.
134 134 """)
135 135
136 136 font_family = Unicode(config=True,
137 137 help="""The font family to use for the console.
138 138 On OSX this defaults to Monaco, on Windows the default is
139 139 Consolas with fallback of Courier, and on other platforms
140 140 the default is Monospace.
141 141 """)
142 142 def _font_family_default(self):
143 143 if sys.platform == 'win32':
144 144 # Consolas ships with Vista/Win7, fallback to Courier if needed
145 145 return 'Consolas'
146 146 elif sys.platform == 'darwin':
147 147 # OSX always has Monaco, no need for a fallback
148 148 return 'Monaco'
149 149 else:
150 150 # Monospace should always exist, no need for a fallback
151 151 return 'Monospace'
152 152
153 153 font_size = Integer(config=True,
154 154 help="""The font size. If unconfigured, Qt will be entrusted
155 155 with the size of the font.
156 156 """)
157 157
158 158 # Whether to override ShortcutEvents for the keybindings defined by this
159 159 # widget (Ctrl+n, Ctrl+a, etc). Enable this if you want this widget to take
160 160 # priority (when it has focus) over, e.g., window-level menu shortcuts.
161 161 override_shortcuts = Bool(False)
162 162
163 163 # ------ Custom Qt Widgets -------------------------------------------------
164 164
165 165 # For other projects to easily override the Qt widgets used by the console
166 166 # (e.g. Spyder)
167 167 custom_control = None
168 168 custom_page_control = None
169 169
170 170 #------ Signals ------------------------------------------------------------
171 171
172 172 # Signals that indicate ConsoleWidget state.
173 173 copy_available = QtCore.Signal(bool)
174 174 redo_available = QtCore.Signal(bool)
175 175 undo_available = QtCore.Signal(bool)
176 176
177 177 # Signal emitted when paging is needed and the paging style has been
178 178 # specified as 'custom'.
179 179 custom_page_requested = QtCore.Signal(object)
180 180
181 181 # Signal emitted when the font is changed.
182 182 font_changed = QtCore.Signal(QtGui.QFont)
183 183
184 184 #------ Protected class variables ------------------------------------------
185 185
186 186 # control handles
187 187 _control = None
188 188 _page_control = None
189 189 _splitter = None
190 190
191 191 # When the control key is down, these keys are mapped.
192 192 _ctrl_down_remap = { QtCore.Qt.Key_B : QtCore.Qt.Key_Left,
193 193 QtCore.Qt.Key_F : QtCore.Qt.Key_Right,
194 194 QtCore.Qt.Key_A : QtCore.Qt.Key_Home,
195 195 QtCore.Qt.Key_P : QtCore.Qt.Key_Up,
196 196 QtCore.Qt.Key_N : QtCore.Qt.Key_Down,
197 197 QtCore.Qt.Key_H : QtCore.Qt.Key_Backspace, }
198 198 if not sys.platform == 'darwin':
199 199 # On OS X, Ctrl-E already does the right thing, whereas End moves the
200 200 # cursor to the bottom of the buffer.
201 201 _ctrl_down_remap[QtCore.Qt.Key_E] = QtCore.Qt.Key_End
202 202
203 203 # The shortcuts defined by this widget. We need to keep track of these to
204 204 # support 'override_shortcuts' above.
205 205 _shortcuts = set(_ctrl_down_remap.keys() +
206 206 [ QtCore.Qt.Key_C, QtCore.Qt.Key_G, QtCore.Qt.Key_O,
207 207 QtCore.Qt.Key_V ])
208 208
209 209 _temp_buffer_filled = False
210 210
211 211 #---------------------------------------------------------------------------
212 212 # 'QObject' interface
213 213 #---------------------------------------------------------------------------
214 214
215 215 def __init__(self, parent=None, **kw):
216 216 """ Create a ConsoleWidget.
217 217
218 218 Parameters:
219 219 -----------
220 220 parent : QWidget, optional [default None]
221 221 The parent for this widget.
222 222 """
223 223 QtGui.QWidget.__init__(self, parent)
224 224 LoggingConfigurable.__init__(self, **kw)
225 225
226 226 # While scrolling the pager on Mac OS X, it tears badly. The
227 227 # NativeGesture is platform and perhaps build-specific hence
228 228 # we take adequate precautions here.
229 229 self._pager_scroll_events = [QtCore.QEvent.Wheel]
230 230 if hasattr(QtCore.QEvent, 'NativeGesture'):
231 231 self._pager_scroll_events.append(QtCore.QEvent.NativeGesture)
232 232
233 233 # Create the layout and underlying text widget.
234 234 layout = QtGui.QStackedLayout(self)
235 235 layout.setContentsMargins(0, 0, 0, 0)
236 236 self._control = self._create_control()
237 237 if self.paging in ('hsplit', 'vsplit'):
238 238 self._splitter = QtGui.QSplitter()
239 239 if self.paging == 'hsplit':
240 240 self._splitter.setOrientation(QtCore.Qt.Horizontal)
241 241 else:
242 242 self._splitter.setOrientation(QtCore.Qt.Vertical)
243 243 self._splitter.addWidget(self._control)
244 244 layout.addWidget(self._splitter)
245 245 else:
246 246 layout.addWidget(self._control)
247 247
248 248 # Create the paging widget, if necessary.
249 249 if self.paging in ('inside', 'hsplit', 'vsplit'):
250 250 self._page_control = self._create_page_control()
251 251 if self._splitter:
252 252 self._page_control.hide()
253 253 self._splitter.addWidget(self._page_control)
254 254 else:
255 255 layout.addWidget(self._page_control)
256 256
257 257 # Initialize protected variables. Some variables contain useful state
258 258 # information for subclasses; they should be considered read-only.
259 259 self._append_before_prompt_pos = 0
260 260 self._ansi_processor = QtAnsiCodeProcessor()
261 261 if self.gui_completion == 'ncurses':
262 262 self._completion_widget = CompletionHtml(self)
263 263 elif self.gui_completion == 'droplist':
264 264 self._completion_widget = CompletionWidget(self)
265 265 elif self.gui_completion == 'plain':
266 266 self._completion_widget = CompletionPlain(self)
267 267
268 268 self._continuation_prompt = '> '
269 269 self._continuation_prompt_html = None
270 270 self._executing = False
271 self._filter_drag = False
272 271 self._filter_resize = False
273 272 self._html_exporter = HtmlExporter(self._control)
274 273 self._input_buffer_executing = ''
275 274 self._input_buffer_pending = ''
276 275 self._kill_ring = QtKillRing(self._control)
277 276 self._prompt = ''
278 277 self._prompt_html = None
279 278 self._prompt_pos = 0
280 279 self._prompt_sep = ''
281 280 self._reading = False
282 281 self._reading_callback = None
283 282 self._tab_width = 8
284 283
285 284 # Set a monospaced font.
286 285 self.reset_font()
287 286
288 287 # Configure actions.
289 288 action = QtGui.QAction('Print', None)
290 289 action.setEnabled(True)
291 290 printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print)
292 291 if printkey.matches("Ctrl+P") and sys.platform != 'darwin':
293 292 # Only override the default if there is a collision.
294 293 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
295 294 printkey = "Ctrl+Shift+P"
296 295 action.setShortcut(printkey)
297 296 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
298 297 action.triggered.connect(self.print_)
299 298 self.addAction(action)
300 299 self.print_action = action
301 300
302 301 action = QtGui.QAction('Save as HTML/XML', None)
303 302 action.setShortcut(QtGui.QKeySequence.Save)
304 303 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
305 304 action.triggered.connect(self.export_html)
306 305 self.addAction(action)
307 306 self.export_action = action
308 307
309 308 action = QtGui.QAction('Select All', None)
310 309 action.setEnabled(True)
311 310 selectall = QtGui.QKeySequence(QtGui.QKeySequence.SelectAll)
312 311 if selectall.matches("Ctrl+A") and sys.platform != 'darwin':
313 312 # Only override the default if there is a collision.
314 313 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
315 314 selectall = "Ctrl+Shift+A"
316 315 action.setShortcut(selectall)
317 316 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
318 317 action.triggered.connect(self.select_all)
319 318 self.addAction(action)
320 319 self.select_all_action = action
321 320
322 321 self.increase_font_size = QtGui.QAction("Bigger Font",
323 322 self,
324 323 shortcut=QtGui.QKeySequence.ZoomIn,
325 324 shortcutContext=QtCore.Qt.WidgetWithChildrenShortcut,
326 325 statusTip="Increase the font size by one point",
327 326 triggered=self._increase_font_size)
328 327 self.addAction(self.increase_font_size)
329 328
330 329 self.decrease_font_size = QtGui.QAction("Smaller Font",
331 330 self,
332 331 shortcut=QtGui.QKeySequence.ZoomOut,
333 332 shortcutContext=QtCore.Qt.WidgetWithChildrenShortcut,
334 333 statusTip="Decrease the font size by one point",
335 334 triggered=self._decrease_font_size)
336 335 self.addAction(self.decrease_font_size)
337 336
338 337 self.reset_font_size = QtGui.QAction("Normal Font",
339 338 self,
340 339 shortcut="Ctrl+0",
341 340 shortcutContext=QtCore.Qt.WidgetWithChildrenShortcut,
342 341 statusTip="Restore the Normal font size",
343 342 triggered=self.reset_font)
344 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 391 def eventFilter(self, obj, event):
349 392 """ Reimplemented to ensure a console-like behavior in the underlying
350 393 text widgets.
351 394 """
352 395 etype = event.type()
353 396 if etype == QtCore.QEvent.KeyPress:
354 397
355 398 # Re-map keys for all filtered widgets.
356 399 key = event.key()
357 400 if self._control_key_down(event.modifiers()) and \
358 401 key in self._ctrl_down_remap:
359 402 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
360 403 self._ctrl_down_remap[key],
361 404 QtCore.Qt.NoModifier)
362 405 QtGui.qApp.sendEvent(obj, new_event)
363 406 return True
364 407
365 408 elif obj == self._control:
366 409 return self._event_filter_console_keypress(event)
367 410
368 411 elif obj == self._page_control:
369 412 return self._event_filter_page_keypress(event)
370 413
371 414 # Make middle-click paste safe.
372 415 elif etype == QtCore.QEvent.MouseButtonRelease and \
373 416 event.button() == QtCore.Qt.MidButton and \
374 417 obj == self._control.viewport():
375 418 cursor = self._control.cursorForPosition(event.pos())
376 419 self._control.setTextCursor(cursor)
377 420 self.paste(QtGui.QClipboard.Selection)
378 421 return True
379 422
380 423 # Manually adjust the scrollbars *after* a resize event is dispatched.
381 424 elif etype == QtCore.QEvent.Resize and not self._filter_resize:
382 425 self._filter_resize = True
383 426 QtGui.qApp.sendEvent(obj, event)
384 427 self._adjust_scrollbars()
385 428 self._filter_resize = False
386 429 return True
387 430
388 431 # Override shortcuts for all filtered widgets.
389 432 elif etype == QtCore.QEvent.ShortcutOverride and \
390 433 self.override_shortcuts and \
391 434 self._control_key_down(event.modifiers()) and \
392 435 event.key() in self._shortcuts:
393 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 438 # Handle scrolling of the vsplit pager. This hack attempts to solve
429 439 # problems with tearing of the help text inside the pager window. This
430 440 # happens only on Mac OS X with both PySide and PyQt. This fix isn't
431 441 # perfect but makes the pager more usable.
432 442 elif etype in self._pager_scroll_events and \
433 443 obj == self._page_control:
434 444 self._page_control.repaint()
435 445 return True
436 446
437 447 elif etype == QtCore.QEvent.MouseMove:
438 448 anchor = self._control.anchorAt(event.pos())
439 449 QtGui.QToolTip.showText(event.globalPos(), anchor)
440 450
441 451 return super(ConsoleWidget, self).eventFilter(obj, event)
442 452
443 453 #---------------------------------------------------------------------------
444 454 # 'QWidget' interface
445 455 #---------------------------------------------------------------------------
446 456
447 457 def sizeHint(self):
448 458 """ Reimplemented to suggest a size that is 80 characters wide and
449 459 25 lines high.
450 460 """
451 461 font_metrics = QtGui.QFontMetrics(self.font)
452 462 margin = (self._control.frameWidth() +
453 463 self._control.document().documentMargin()) * 2
454 464 style = self.style()
455 465 splitwidth = style.pixelMetric(QtGui.QStyle.PM_SplitterWidth)
456 466
457 467 # Note 1: Despite my best efforts to take the various margins into
458 468 # account, the width is still coming out a bit too small, so we include
459 469 # a fudge factor of one character here.
460 470 # Note 2: QFontMetrics.maxWidth is not used here or anywhere else due
461 471 # to a Qt bug on certain Mac OS systems where it returns 0.
462 472 width = font_metrics.width(' ') * 81 + margin
463 473 width += style.pixelMetric(QtGui.QStyle.PM_ScrollBarExtent)
464 474 if self.paging == 'hsplit':
465 475 width = width * 2 + splitwidth
466 476
467 477 height = font_metrics.height() * 25 + margin
468 478 if self.paging == 'vsplit':
469 479 height = height * 2 + splitwidth
470 480
471 481 return QtCore.QSize(width, height)
472 482
473 483 #---------------------------------------------------------------------------
474 484 # 'ConsoleWidget' public interface
475 485 #---------------------------------------------------------------------------
476 486
477 487 def can_copy(self):
478 488 """ Returns whether text can be copied to the clipboard.
479 489 """
480 490 return self._control.textCursor().hasSelection()
481 491
482 492 def can_cut(self):
483 493 """ Returns whether text can be cut to the clipboard.
484 494 """
485 495 cursor = self._control.textCursor()
486 496 return (cursor.hasSelection() and
487 497 self._in_buffer(cursor.anchor()) and
488 498 self._in_buffer(cursor.position()))
489 499
490 500 def can_paste(self):
491 501 """ Returns whether text can be pasted from the clipboard.
492 502 """
493 503 if self._control.textInteractionFlags() & QtCore.Qt.TextEditable:
494 504 return bool(QtGui.QApplication.clipboard().text())
495 505 return False
496 506
497 507 def clear(self, keep_input=True):
498 508 """ Clear the console.
499 509
500 510 Parameters:
501 511 -----------
502 512 keep_input : bool, optional (default True)
503 513 If set, restores the old input buffer if a new prompt is written.
504 514 """
505 515 if self._executing:
506 516 self._control.clear()
507 517 else:
508 518 if keep_input:
509 519 input_buffer = self.input_buffer
510 520 self._control.clear()
511 521 self._show_prompt()
512 522 if keep_input:
513 523 self.input_buffer = input_buffer
514 524
515 525 def copy(self):
516 526 """ Copy the currently selected text to the clipboard.
517 527 """
518 528 self.layout().currentWidget().copy()
519 529
520 530 def copy_anchor(self, anchor):
521 531 """ Copy anchor text to the clipboard
522 532 """
523 533 QtGui.QApplication.clipboard().setText(anchor)
524 534
525 535 def cut(self):
526 536 """ Copy the currently selected text to the clipboard and delete it
527 537 if it's inside the input buffer.
528 538 """
529 539 self.copy()
530 540 if self.can_cut():
531 541 self._control.textCursor().removeSelectedText()
532 542
533 543 def execute(self, source=None, hidden=False, interactive=False):
534 544 """ Executes source or the input buffer, possibly prompting for more
535 545 input.
536 546
537 547 Parameters:
538 548 -----------
539 549 source : str, optional
540 550
541 551 The source to execute. If not specified, the input buffer will be
542 552 used. If specified and 'hidden' is False, the input buffer will be
543 553 replaced with the source before execution.
544 554
545 555 hidden : bool, optional (default False)
546 556
547 557 If set, no output will be shown and the prompt will not be modified.
548 558 In other words, it will be completely invisible to the user that
549 559 an execution has occurred.
550 560
551 561 interactive : bool, optional (default False)
552 562
553 563 Whether the console is to treat the source as having been manually
554 564 entered by the user. The effect of this parameter depends on the
555 565 subclass implementation.
556 566
557 567 Raises:
558 568 -------
559 569 RuntimeError
560 570 If incomplete input is given and 'hidden' is True. In this case,
561 571 it is not possible to prompt for more input.
562 572
563 573 Returns:
564 574 --------
565 575 A boolean indicating whether the source was executed.
566 576 """
567 577 # WARNING: The order in which things happen here is very particular, in
568 578 # large part because our syntax highlighting is fragile. If you change
569 579 # something, test carefully!
570 580
571 581 # Decide what to execute.
572 582 if source is None:
573 583 source = self.input_buffer
574 584 if not hidden:
575 585 # A newline is appended later, but it should be considered part
576 586 # of the input buffer.
577 587 source += '\n'
578 588 elif not hidden:
579 589 self.input_buffer = source
580 590
581 591 # Execute the source or show a continuation prompt if it is incomplete.
582 592 complete = self._is_complete(source, interactive)
583 593 if hidden:
584 594 if complete:
585 595 self._execute(source, hidden)
586 596 else:
587 597 error = 'Incomplete noninteractive input: "%s"'
588 598 raise RuntimeError(error % source)
589 599 else:
590 600 if complete:
591 601 self._append_plain_text('\n')
592 602 self._input_buffer_executing = self.input_buffer
593 603 self._executing = True
594 604 self._prompt_finished()
595 605
596 606 # The maximum block count is only in effect during execution.
597 607 # This ensures that _prompt_pos does not become invalid due to
598 608 # text truncation.
599 609 self._control.document().setMaximumBlockCount(self.buffer_size)
600 610
601 611 # Setting a positive maximum block count will automatically
602 612 # disable the undo/redo history, but just to be safe:
603 613 self._control.setUndoRedoEnabled(False)
604 614
605 615 # Perform actual execution.
606 616 self._execute(source, hidden)
607 617
608 618 else:
609 619 # Do this inside an edit block so continuation prompts are
610 620 # removed seamlessly via undo/redo.
611 621 cursor = self._get_end_cursor()
612 622 cursor.beginEditBlock()
613 623 cursor.insertText('\n')
614 624 self._insert_continuation_prompt(cursor)
615 625 cursor.endEditBlock()
616 626
617 627 # Do not do this inside the edit block. It works as expected
618 628 # when using a QPlainTextEdit control, but does not have an
619 629 # effect when using a QTextEdit. I believe this is a Qt bug.
620 630 self._control.moveCursor(QtGui.QTextCursor.End)
621 631
622 632 return complete
623 633
624 634 def export_html(self):
625 635 """ Shows a dialog to export HTML/XML in various formats.
626 636 """
627 637 self._html_exporter.export()
628 638
629 639 def _get_input_buffer(self, force=False):
630 640 """ The text that the user has entered entered at the current prompt.
631 641
632 642 If the console is currently executing, the text that is executing will
633 643 always be returned.
634 644 """
635 645 # If we're executing, the input buffer may not even exist anymore due to
636 646 # the limit imposed by 'buffer_size'. Therefore, we store it.
637 647 if self._executing and not force:
638 648 return self._input_buffer_executing
639 649
640 650 cursor = self._get_end_cursor()
641 651 cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.KeepAnchor)
642 652 input_buffer = cursor.selection().toPlainText()
643 653
644 654 # Strip out continuation prompts.
645 655 return input_buffer.replace('\n' + self._continuation_prompt, '\n')
646 656
647 657 def _set_input_buffer(self, string):
648 658 """ Sets the text in the input buffer.
649 659
650 660 If the console is currently executing, this call has no *immediate*
651 661 effect. When the execution is finished, the input buffer will be updated
652 662 appropriately.
653 663 """
654 664 # If we're executing, store the text for later.
655 665 if self._executing:
656 666 self._input_buffer_pending = string
657 667 return
658 668
659 669 # Remove old text.
660 670 cursor = self._get_end_cursor()
661 671 cursor.beginEditBlock()
662 672 cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.KeepAnchor)
663 673 cursor.removeSelectedText()
664 674
665 675 # Insert new text with continuation prompts.
666 676 self._insert_plain_text_into_buffer(self._get_prompt_cursor(), string)
667 677 cursor.endEditBlock()
668 678 self._control.moveCursor(QtGui.QTextCursor.End)
669 679
670 680 input_buffer = property(_get_input_buffer, _set_input_buffer)
671 681
672 682 def _get_font(self):
673 683 """ The base font being used by the ConsoleWidget.
674 684 """
675 685 return self._control.document().defaultFont()
676 686
677 687 def _set_font(self, font):
678 688 """ Sets the base font for the ConsoleWidget to the specified QFont.
679 689 """
680 690 font_metrics = QtGui.QFontMetrics(font)
681 691 self._control.setTabStopWidth(self.tab_width * font_metrics.width(' '))
682 692
683 693 self._completion_widget.setFont(font)
684 694 self._control.document().setDefaultFont(font)
685 695 if self._page_control:
686 696 self._page_control.document().setDefaultFont(font)
687 697
688 698 self.font_changed.emit(font)
689 699
690 700 font = property(_get_font, _set_font)
691 701
692 702 def open_anchor(self, anchor):
693 703 """ Open selected anchor in the default webbrowser
694 704 """
695 705 webbrowser.open( anchor )
696 706
697 707 def paste(self, mode=QtGui.QClipboard.Clipboard):
698 708 """ Paste the contents of the clipboard into the input region.
699 709
700 710 Parameters:
701 711 -----------
702 712 mode : QClipboard::Mode, optional [default QClipboard::Clipboard]
703 713
704 714 Controls which part of the system clipboard is used. This can be
705 715 used to access the selection clipboard in X11 and the Find buffer
706 716 in Mac OS. By default, the regular clipboard is used.
707 717 """
708 718 if self._control.textInteractionFlags() & QtCore.Qt.TextEditable:
709 719 # Make sure the paste is safe.
710 720 self._keep_cursor_in_buffer()
711 721 cursor = self._control.textCursor()
712 722
713 723 # Remove any trailing newline, which confuses the GUI and forces the
714 724 # user to backspace.
715 725 text = QtGui.QApplication.clipboard().text(mode).rstrip()
716 726 self._insert_plain_text_into_buffer(cursor, dedent(text))
717 727
718 728 def print_(self, printer = None):
719 729 """ Print the contents of the ConsoleWidget to the specified QPrinter.
720 730 """
721 731 if (not printer):
722 732 printer = QtGui.QPrinter()
723 733 if(QtGui.QPrintDialog(printer).exec_() != QtGui.QDialog.Accepted):
724 734 return
725 735 self._control.print_(printer)
726 736
727 737 def prompt_to_top(self):
728 738 """ Moves the prompt to the top of the viewport.
729 739 """
730 740 if not self._executing:
731 741 prompt_cursor = self._get_prompt_cursor()
732 742 if self._get_cursor().blockNumber() < prompt_cursor.blockNumber():
733 743 self._set_cursor(prompt_cursor)
734 744 self._set_top_cursor(prompt_cursor)
735 745
736 746 def redo(self):
737 747 """ Redo the last operation. If there is no operation to redo, nothing
738 748 happens.
739 749 """
740 750 self._control.redo()
741 751
742 752 def reset_font(self):
743 753 """ Sets the font to the default fixed-width font for this platform.
744 754 """
745 755 if sys.platform == 'win32':
746 756 # Consolas ships with Vista/Win7, fallback to Courier if needed
747 757 fallback = 'Courier'
748 758 elif sys.platform == 'darwin':
749 759 # OSX always has Monaco
750 760 fallback = 'Monaco'
751 761 else:
752 762 # Monospace should always exist
753 763 fallback = 'Monospace'
754 764 font = get_font(self.font_family, fallback)
755 765 if self.font_size:
756 766 font.setPointSize(self.font_size)
757 767 else:
758 768 font.setPointSize(QtGui.qApp.font().pointSize())
759 769 font.setStyleHint(QtGui.QFont.TypeWriter)
760 770 self._set_font(font)
761 771
762 772 def change_font_size(self, delta):
763 773 """Change the font size by the specified amount (in points).
764 774 """
765 775 font = self.font
766 776 size = max(font.pointSize() + delta, 1) # minimum 1 point
767 777 font.setPointSize(size)
768 778 self._set_font(font)
769 779
770 780 def _increase_font_size(self):
771 781 self.change_font_size(1)
772 782
773 783 def _decrease_font_size(self):
774 784 self.change_font_size(-1)
775 785
776 786 def select_all(self):
777 787 """ Selects all the text in the buffer.
778 788 """
779 789 self._control.selectAll()
780 790
781 791 def _get_tab_width(self):
782 792 """ The width (in terms of space characters) for tab characters.
783 793 """
784 794 return self._tab_width
785 795
786 796 def _set_tab_width(self, tab_width):
787 797 """ Sets the width (in terms of space characters) for tab characters.
788 798 """
789 799 font_metrics = QtGui.QFontMetrics(self.font)
790 800 self._control.setTabStopWidth(tab_width * font_metrics.width(' '))
791 801
792 802 self._tab_width = tab_width
793 803
794 804 tab_width = property(_get_tab_width, _set_tab_width)
795 805
796 806 def undo(self):
797 807 """ Undo the last operation. If there is no operation to undo, nothing
798 808 happens.
799 809 """
800 810 self._control.undo()
801 811
802 812 #---------------------------------------------------------------------------
803 813 # 'ConsoleWidget' abstract interface
804 814 #---------------------------------------------------------------------------
805 815
806 816 def _is_complete(self, source, interactive):
807 817 """ Returns whether 'source' can be executed. When triggered by an
808 818 Enter/Return key press, 'interactive' is True; otherwise, it is
809 819 False.
810 820 """
811 821 raise NotImplementedError
812 822
813 823 def _execute(self, source, hidden):
814 824 """ Execute 'source'. If 'hidden', do not show any output.
815 825 """
816 826 raise NotImplementedError
817 827
818 828 def _prompt_started_hook(self):
819 829 """ Called immediately after a new prompt is displayed.
820 830 """
821 831 pass
822 832
823 833 def _prompt_finished_hook(self):
824 834 """ Called immediately after a prompt is finished, i.e. when some input
825 835 will be processed and a new prompt displayed.
826 836 """
827 837 pass
828 838
829 839 def _up_pressed(self, shift_modifier):
830 840 """ Called when the up key is pressed. Returns whether to continue
831 841 processing the event.
832 842 """
833 843 return True
834 844
835 845 def _down_pressed(self, shift_modifier):
836 846 """ Called when the down key is pressed. Returns whether to continue
837 847 processing the event.
838 848 """
839 849 return True
840 850
841 851 def _tab_pressed(self):
842 852 """ Called when the tab key is pressed. Returns whether to continue
843 853 processing the event.
844 854 """
845 855 return False
846 856
847 857 #--------------------------------------------------------------------------
848 858 # 'ConsoleWidget' protected interface
849 859 #--------------------------------------------------------------------------
850 860
851 861 def _append_custom(self, insert, input, before_prompt=False):
852 862 """ A low-level method for appending content to the end of the buffer.
853 863
854 864 If 'before_prompt' is enabled, the content will be inserted before the
855 865 current prompt, if there is one.
856 866 """
857 867 # Determine where to insert the content.
858 868 cursor = self._control.textCursor()
859 869 if before_prompt and (self._reading or not self._executing):
860 870 cursor.setPosition(self._append_before_prompt_pos)
861 871 else:
862 872 cursor.movePosition(QtGui.QTextCursor.End)
863 873 start_pos = cursor.position()
864 874
865 875 # Perform the insertion.
866 876 result = insert(cursor, input)
867 877
868 878 # Adjust the prompt position if we have inserted before it. This is safe
869 879 # because buffer truncation is disabled when not executing.
870 880 if before_prompt and not self._executing:
871 881 diff = cursor.position() - start_pos
872 882 self._append_before_prompt_pos += diff
873 883 self._prompt_pos += diff
874 884
875 885 return result
876 886
877 887 def _append_block(self, block_format=None, before_prompt=False):
878 888 """ Appends an new QTextBlock to the end of the console buffer.
879 889 """
880 890 self._append_custom(self._insert_block, block_format, before_prompt)
881 891
882 892 def _append_html(self, html, before_prompt=False):
883 893 """ Appends HTML at the end of the console buffer.
884 894 """
885 895 self._append_custom(self._insert_html, html, before_prompt)
886 896
887 897 def _append_html_fetching_plain_text(self, html, before_prompt=False):
888 898 """ Appends HTML, then returns the plain text version of it.
889 899 """
890 900 return self._append_custom(self._insert_html_fetching_plain_text,
891 901 html, before_prompt)
892 902
893 903 def _append_plain_text(self, text, before_prompt=False):
894 904 """ Appends plain text, processing ANSI codes if enabled.
895 905 """
896 906 self._append_custom(self._insert_plain_text, text, before_prompt)
897 907
898 908 def _cancel_completion(self):
899 909 """ If text completion is progress, cancel it.
900 910 """
901 911 self._completion_widget.cancel_completion()
902 912
903 913 def _clear_temporary_buffer(self):
904 914 """ Clears the "temporary text" buffer, i.e. all the text following
905 915 the prompt region.
906 916 """
907 917 # Select and remove all text below the input buffer.
908 918 cursor = self._get_prompt_cursor()
909 919 prompt = self._continuation_prompt.lstrip()
910 920 if(self._temp_buffer_filled):
911 921 self._temp_buffer_filled = False
912 922 while cursor.movePosition(QtGui.QTextCursor.NextBlock):
913 923 temp_cursor = QtGui.QTextCursor(cursor)
914 924 temp_cursor.select(QtGui.QTextCursor.BlockUnderCursor)
915 925 text = temp_cursor.selection().toPlainText().lstrip()
916 926 if not text.startswith(prompt):
917 927 break
918 928 else:
919 929 # We've reached the end of the input buffer and no text follows.
920 930 return
921 931 cursor.movePosition(QtGui.QTextCursor.Left) # Grab the newline.
922 932 cursor.movePosition(QtGui.QTextCursor.End,
923 933 QtGui.QTextCursor.KeepAnchor)
924 934 cursor.removeSelectedText()
925 935
926 936 # After doing this, we have no choice but to clear the undo/redo
927 937 # history. Otherwise, the text is not "temporary" at all, because it
928 938 # can be recalled with undo/redo. Unfortunately, Qt does not expose
929 939 # fine-grained control to the undo/redo system.
930 940 if self._control.isUndoRedoEnabled():
931 941 self._control.setUndoRedoEnabled(False)
932 942 self._control.setUndoRedoEnabled(True)
933 943
934 944 def _complete_with_items(self, cursor, items):
935 945 """ Performs completion with 'items' at the specified cursor location.
936 946 """
937 947 self._cancel_completion()
938 948
939 949 if len(items) == 1:
940 950 cursor.setPosition(self._control.textCursor().position(),
941 951 QtGui.QTextCursor.KeepAnchor)
942 952 cursor.insertText(items[0])
943 953
944 954 elif len(items) > 1:
945 955 current_pos = self._control.textCursor().position()
946 956 prefix = commonprefix(items)
947 957 if prefix:
948 958 cursor.setPosition(current_pos, QtGui.QTextCursor.KeepAnchor)
949 959 cursor.insertText(prefix)
950 960 current_pos = cursor.position()
951 961
952 962 cursor.movePosition(QtGui.QTextCursor.Left, n=len(prefix))
953 963 self._completion_widget.show_items(cursor, items)
954 964
955 965
956 966 def _fill_temporary_buffer(self, cursor, text, html=False):
957 967 """fill the area below the active editting zone with text"""
958 968
959 969 current_pos = self._control.textCursor().position()
960 970
961 971 cursor.beginEditBlock()
962 972 self._append_plain_text('\n')
963 973 self._page(text, html=html)
964 974 cursor.endEditBlock()
965 975
966 976 cursor.setPosition(current_pos)
967 977 self._control.moveCursor(QtGui.QTextCursor.End)
968 978 self._control.setTextCursor(cursor)
969 979
970 980 self._temp_buffer_filled = True
971 981
972 982
973 983 def _context_menu_make(self, pos):
974 984 """ Creates a context menu for the given QPoint (in widget coordinates).
975 985 """
976 986 menu = QtGui.QMenu(self)
977 987
978 988 self.cut_action = menu.addAction('Cut', self.cut)
979 989 self.cut_action.setEnabled(self.can_cut())
980 990 self.cut_action.setShortcut(QtGui.QKeySequence.Cut)
981 991
982 992 self.copy_action = menu.addAction('Copy', self.copy)
983 993 self.copy_action.setEnabled(self.can_copy())
984 994 self.copy_action.setShortcut(QtGui.QKeySequence.Copy)
985 995
986 996 self.paste_action = menu.addAction('Paste', self.paste)
987 997 self.paste_action.setEnabled(self.can_paste())
988 998 self.paste_action.setShortcut(QtGui.QKeySequence.Paste)
989 999
990 1000 anchor = self._control.anchorAt(pos)
991 1001 if anchor:
992 1002 menu.addSeparator()
993 1003 self.copy_link_action = menu.addAction(
994 1004 'Copy Link Address', lambda: self.copy_anchor(anchor=anchor))
995 1005 self.open_link_action = menu.addAction(
996 1006 'Open Link', lambda: self.open_anchor(anchor=anchor))
997 1007
998 1008 menu.addSeparator()
999 1009 menu.addAction(self.select_all_action)
1000 1010
1001 1011 menu.addSeparator()
1002 1012 menu.addAction(self.export_action)
1003 1013 menu.addAction(self.print_action)
1004 1014
1005 1015 return menu
1006 1016
1007 1017 def _control_key_down(self, modifiers, include_command=False):
1008 1018 """ Given a KeyboardModifiers flags object, return whether the Control
1009 1019 key is down.
1010 1020
1011 1021 Parameters:
1012 1022 -----------
1013 1023 include_command : bool, optional (default True)
1014 1024 Whether to treat the Command key as a (mutually exclusive) synonym
1015 1025 for Control when in Mac OS.
1016 1026 """
1017 1027 # Note that on Mac OS, ControlModifier corresponds to the Command key
1018 1028 # while MetaModifier corresponds to the Control key.
1019 1029 if sys.platform == 'darwin':
1020 1030 down = include_command and (modifiers & QtCore.Qt.ControlModifier)
1021 1031 return bool(down) ^ bool(modifiers & QtCore.Qt.MetaModifier)
1022 1032 else:
1023 1033 return bool(modifiers & QtCore.Qt.ControlModifier)
1024 1034
1025 1035 def _create_control(self):
1026 1036 """ Creates and connects the underlying text widget.
1027 1037 """
1028 1038 # Create the underlying control.
1029 1039 if self.custom_control:
1030 1040 control = self.custom_control()
1031 1041 elif self.kind == 'plain':
1032 1042 control = QtGui.QPlainTextEdit()
1033 1043 elif self.kind == 'rich':
1034 1044 control = QtGui.QTextEdit()
1035 1045 control.setAcceptRichText(False)
1036 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 1052 # Install event filters. The filter on the viewport is needed for
1039 # mouse events and drag events.
1053 # mouse events.
1040 1054 control.installEventFilter(self)
1041 1055 control.viewport().installEventFilter(self)
1042 1056
1043 1057 # Connect signals.
1044 1058 control.customContextMenuRequested.connect(
1045 1059 self._custom_context_menu_requested)
1046 1060 control.copyAvailable.connect(self.copy_available)
1047 1061 control.redoAvailable.connect(self.redo_available)
1048 1062 control.undoAvailable.connect(self.undo_available)
1049 1063
1050 1064 # Hijack the document size change signal to prevent Qt from adjusting
1051 1065 # the viewport's scrollbar. We are relying on an implementation detail
1052 1066 # of Q(Plain)TextEdit here, which is potentially dangerous, but without
1053 1067 # this functionality we cannot create a nice terminal interface.
1054 1068 layout = control.document().documentLayout()
1055 1069 layout.documentSizeChanged.disconnect()
1056 1070 layout.documentSizeChanged.connect(self._adjust_scrollbars)
1057 1071
1058 1072 # Configure the control.
1059 1073 control.setAttribute(QtCore.Qt.WA_InputMethodEnabled, True)
1060 1074 control.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
1061 1075 control.setReadOnly(True)
1062 1076 control.setUndoRedoEnabled(False)
1063 1077 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
1064 1078 return control
1065 1079
1066 1080 def _create_page_control(self):
1067 1081 """ Creates and connects the underlying paging widget.
1068 1082 """
1069 1083 if self.custom_page_control:
1070 1084 control = self.custom_page_control()
1071 1085 elif self.kind == 'plain':
1072 1086 control = QtGui.QPlainTextEdit()
1073 1087 elif self.kind == 'rich':
1074 1088 control = QtGui.QTextEdit()
1075 1089 control.installEventFilter(self)
1076 1090 viewport = control.viewport()
1077 1091 viewport.installEventFilter(self)
1078 1092 control.setReadOnly(True)
1079 1093 control.setUndoRedoEnabled(False)
1080 1094 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
1081 1095 return control
1082 1096
1083 1097 def _event_filter_console_keypress(self, event):
1084 1098 """ Filter key events for the underlying text widget to create a
1085 1099 console-like interface.
1086 1100 """
1087 1101 intercepted = False
1088 1102 cursor = self._control.textCursor()
1089 1103 position = cursor.position()
1090 1104 key = event.key()
1091 1105 ctrl_down = self._control_key_down(event.modifiers())
1092 1106 alt_down = event.modifiers() & QtCore.Qt.AltModifier
1093 1107 shift_down = event.modifiers() & QtCore.Qt.ShiftModifier
1094 1108
1095 1109 #------ Special sequences ----------------------------------------------
1096 1110
1097 1111 if event.matches(QtGui.QKeySequence.Copy):
1098 1112 self.copy()
1099 1113 intercepted = True
1100 1114
1101 1115 elif event.matches(QtGui.QKeySequence.Cut):
1102 1116 self.cut()
1103 1117 intercepted = True
1104 1118
1105 1119 elif event.matches(QtGui.QKeySequence.Paste):
1106 1120 self.paste()
1107 1121 intercepted = True
1108 1122
1109 1123 #------ Special modifier logic -----------------------------------------
1110 1124
1111 1125 elif key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
1112 1126 intercepted = True
1113 1127
1114 1128 # Special handling when tab completing in text mode.
1115 1129 self._cancel_completion()
1116 1130
1117 1131 if self._in_buffer(position):
1118 1132 # Special handling when a reading a line of raw input.
1119 1133 if self._reading:
1120 1134 self._append_plain_text('\n')
1121 1135 self._reading = False
1122 1136 if self._reading_callback:
1123 1137 self._reading_callback()
1124 1138
1125 1139 # If the input buffer is a single line or there is only
1126 1140 # whitespace after the cursor, execute. Otherwise, split the
1127 1141 # line with a continuation prompt.
1128 1142 elif not self._executing:
1129 1143 cursor.movePosition(QtGui.QTextCursor.End,
1130 1144 QtGui.QTextCursor.KeepAnchor)
1131 1145 at_end = len(cursor.selectedText().strip()) == 0
1132 1146 single_line = (self._get_end_cursor().blockNumber() ==
1133 1147 self._get_prompt_cursor().blockNumber())
1134 1148 if (at_end or shift_down or single_line) and not ctrl_down:
1135 1149 self.execute(interactive = not shift_down)
1136 1150 else:
1137 1151 # Do this inside an edit block for clean undo/redo.
1138 1152 cursor.beginEditBlock()
1139 1153 cursor.setPosition(position)
1140 1154 cursor.insertText('\n')
1141 1155 self._insert_continuation_prompt(cursor)
1142 1156 cursor.endEditBlock()
1143 1157
1144 1158 # Ensure that the whole input buffer is visible.
1145 1159 # FIXME: This will not be usable if the input buffer is
1146 1160 # taller than the console widget.
1147 1161 self._control.moveCursor(QtGui.QTextCursor.End)
1148 1162 self._control.setTextCursor(cursor)
1149 1163
1150 1164 #------ Control/Cmd modifier -------------------------------------------
1151 1165
1152 1166 elif ctrl_down:
1153 1167 if key == QtCore.Qt.Key_G:
1154 1168 self._keyboard_quit()
1155 1169 intercepted = True
1156 1170
1157 1171 elif key == QtCore.Qt.Key_K:
1158 1172 if self._in_buffer(position):
1159 1173 cursor.clearSelection()
1160 1174 cursor.movePosition(QtGui.QTextCursor.EndOfLine,
1161 1175 QtGui.QTextCursor.KeepAnchor)
1162 1176 if not cursor.hasSelection():
1163 1177 # Line deletion (remove continuation prompt)
1164 1178 cursor.movePosition(QtGui.QTextCursor.NextBlock,
1165 1179 QtGui.QTextCursor.KeepAnchor)
1166 1180 cursor.movePosition(QtGui.QTextCursor.Right,
1167 1181 QtGui.QTextCursor.KeepAnchor,
1168 1182 len(self._continuation_prompt))
1169 1183 self._kill_ring.kill_cursor(cursor)
1170 1184 self._set_cursor(cursor)
1171 1185 intercepted = True
1172 1186
1173 1187 elif key == QtCore.Qt.Key_L:
1174 1188 self.prompt_to_top()
1175 1189 intercepted = True
1176 1190
1177 1191 elif key == QtCore.Qt.Key_O:
1178 1192 if self._page_control and self._page_control.isVisible():
1179 1193 self._page_control.setFocus()
1180 1194 intercepted = True
1181 1195
1182 1196 elif key == QtCore.Qt.Key_U:
1183 1197 if self._in_buffer(position):
1184 1198 cursor.clearSelection()
1185 1199 start_line = cursor.blockNumber()
1186 1200 if start_line == self._get_prompt_cursor().blockNumber():
1187 1201 offset = len(self._prompt)
1188 1202 else:
1189 1203 offset = len(self._continuation_prompt)
1190 1204 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
1191 1205 QtGui.QTextCursor.KeepAnchor)
1192 1206 cursor.movePosition(QtGui.QTextCursor.Right,
1193 1207 QtGui.QTextCursor.KeepAnchor, offset)
1194 1208 self._kill_ring.kill_cursor(cursor)
1195 1209 self._set_cursor(cursor)
1196 1210 intercepted = True
1197 1211
1198 1212 elif key == QtCore.Qt.Key_Y:
1199 1213 self._keep_cursor_in_buffer()
1200 1214 self._kill_ring.yank()
1201 1215 intercepted = True
1202 1216
1203 1217 elif key in (QtCore.Qt.Key_Backspace, QtCore.Qt.Key_Delete):
1204 1218 if key == QtCore.Qt.Key_Backspace:
1205 1219 cursor = self._get_word_start_cursor(position)
1206 1220 else: # key == QtCore.Qt.Key_Delete
1207 1221 cursor = self._get_word_end_cursor(position)
1208 1222 cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor)
1209 1223 self._kill_ring.kill_cursor(cursor)
1210 1224 intercepted = True
1211 1225
1212 1226 elif key == QtCore.Qt.Key_D:
1213 1227 if len(self.input_buffer) == 0:
1214 1228 self.exit_requested.emit(self)
1215 1229 else:
1216 1230 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1217 1231 QtCore.Qt.Key_Delete,
1218 1232 QtCore.Qt.NoModifier)
1219 1233 QtGui.qApp.sendEvent(self._control, new_event)
1220 1234 intercepted = True
1221 1235
1222 1236 #------ Alt modifier ---------------------------------------------------
1223 1237
1224 1238 elif alt_down:
1225 1239 if key == QtCore.Qt.Key_B:
1226 1240 self._set_cursor(self._get_word_start_cursor(position))
1227 1241 intercepted = True
1228 1242
1229 1243 elif key == QtCore.Qt.Key_F:
1230 1244 self._set_cursor(self._get_word_end_cursor(position))
1231 1245 intercepted = True
1232 1246
1233 1247 elif key == QtCore.Qt.Key_Y:
1234 1248 self._kill_ring.rotate()
1235 1249 intercepted = True
1236 1250
1237 1251 elif key == QtCore.Qt.Key_Backspace:
1238 1252 cursor = self._get_word_start_cursor(position)
1239 1253 cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor)
1240 1254 self._kill_ring.kill_cursor(cursor)
1241 1255 intercepted = True
1242 1256
1243 1257 elif key == QtCore.Qt.Key_D:
1244 1258 cursor = self._get_word_end_cursor(position)
1245 1259 cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor)
1246 1260 self._kill_ring.kill_cursor(cursor)
1247 1261 intercepted = True
1248 1262
1249 1263 elif key == QtCore.Qt.Key_Delete:
1250 1264 intercepted = True
1251 1265
1252 1266 elif key == QtCore.Qt.Key_Greater:
1253 1267 self._control.moveCursor(QtGui.QTextCursor.End)
1254 1268 intercepted = True
1255 1269
1256 1270 elif key == QtCore.Qt.Key_Less:
1257 1271 self._control.setTextCursor(self._get_prompt_cursor())
1258 1272 intercepted = True
1259 1273
1260 1274 #------ No modifiers ---------------------------------------------------
1261 1275
1262 1276 else:
1263 1277 if shift_down:
1264 1278 anchormode = QtGui.QTextCursor.KeepAnchor
1265 1279 else:
1266 1280 anchormode = QtGui.QTextCursor.MoveAnchor
1267 1281
1268 1282 if key == QtCore.Qt.Key_Escape:
1269 1283 self._keyboard_quit()
1270 1284 intercepted = True
1271 1285
1272 1286 elif key == QtCore.Qt.Key_Up:
1273 1287 if self._reading or not self._up_pressed(shift_down):
1274 1288 intercepted = True
1275 1289 else:
1276 1290 prompt_line = self._get_prompt_cursor().blockNumber()
1277 1291 intercepted = cursor.blockNumber() <= prompt_line
1278 1292
1279 1293 elif key == QtCore.Qt.Key_Down:
1280 1294 if self._reading or not self._down_pressed(shift_down):
1281 1295 intercepted = True
1282 1296 else:
1283 1297 end_line = self._get_end_cursor().blockNumber()
1284 1298 intercepted = cursor.blockNumber() == end_line
1285 1299
1286 1300 elif key == QtCore.Qt.Key_Tab:
1287 1301 if not self._reading:
1288 1302 if self._tab_pressed():
1289 1303 # real tab-key, insert four spaces
1290 1304 cursor.insertText(' '*4)
1291 1305 intercepted = True
1292 1306
1293 1307 elif key == QtCore.Qt.Key_Left:
1294 1308
1295 1309 # Move to the previous line
1296 1310 line, col = cursor.blockNumber(), cursor.columnNumber()
1297 1311 if line > self._get_prompt_cursor().blockNumber() and \
1298 1312 col == len(self._continuation_prompt):
1299 1313 self._control.moveCursor(QtGui.QTextCursor.PreviousBlock,
1300 1314 mode=anchormode)
1301 1315 self._control.moveCursor(QtGui.QTextCursor.EndOfBlock,
1302 1316 mode=anchormode)
1303 1317 intercepted = True
1304 1318
1305 1319 # Regular left movement
1306 1320 else:
1307 1321 intercepted = not self._in_buffer(position - 1)
1308 1322
1309 1323 elif key == QtCore.Qt.Key_Right:
1310 1324 original_block_number = cursor.blockNumber()
1311 1325 cursor.movePosition(QtGui.QTextCursor.Right,
1312 1326 mode=anchormode)
1313 1327 if cursor.blockNumber() != original_block_number:
1314 1328 cursor.movePosition(QtGui.QTextCursor.Right,
1315 1329 n=len(self._continuation_prompt),
1316 1330 mode=anchormode)
1317 1331 self._set_cursor(cursor)
1318 1332 intercepted = True
1319 1333
1320 1334 elif key == QtCore.Qt.Key_Home:
1321 1335 start_line = cursor.blockNumber()
1322 1336 if start_line == self._get_prompt_cursor().blockNumber():
1323 1337 start_pos = self._prompt_pos
1324 1338 else:
1325 1339 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
1326 1340 QtGui.QTextCursor.KeepAnchor)
1327 1341 start_pos = cursor.position()
1328 1342 start_pos += len(self._continuation_prompt)
1329 1343 cursor.setPosition(position)
1330 1344 if shift_down and self._in_buffer(position):
1331 1345 cursor.setPosition(start_pos, QtGui.QTextCursor.KeepAnchor)
1332 1346 else:
1333 1347 cursor.setPosition(start_pos)
1334 1348 self._set_cursor(cursor)
1335 1349 intercepted = True
1336 1350
1337 1351 elif key == QtCore.Qt.Key_Backspace:
1338 1352
1339 1353 # Line deletion (remove continuation prompt)
1340 1354 line, col = cursor.blockNumber(), cursor.columnNumber()
1341 1355 if not self._reading and \
1342 1356 col == len(self._continuation_prompt) and \
1343 1357 line > self._get_prompt_cursor().blockNumber():
1344 1358 cursor.beginEditBlock()
1345 1359 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
1346 1360 QtGui.QTextCursor.KeepAnchor)
1347 1361 cursor.removeSelectedText()
1348 1362 cursor.deletePreviousChar()
1349 1363 cursor.endEditBlock()
1350 1364 intercepted = True
1351 1365
1352 1366 # Regular backwards deletion
1353 1367 else:
1354 1368 anchor = cursor.anchor()
1355 1369 if anchor == position:
1356 1370 intercepted = not self._in_buffer(position - 1)
1357 1371 else:
1358 1372 intercepted = not self._in_buffer(min(anchor, position))
1359 1373
1360 1374 elif key == QtCore.Qt.Key_Delete:
1361 1375
1362 1376 # Line deletion (remove continuation prompt)
1363 1377 if not self._reading and self._in_buffer(position) and \
1364 1378 cursor.atBlockEnd() and not cursor.hasSelection():
1365 1379 cursor.movePosition(QtGui.QTextCursor.NextBlock,
1366 1380 QtGui.QTextCursor.KeepAnchor)
1367 1381 cursor.movePosition(QtGui.QTextCursor.Right,
1368 1382 QtGui.QTextCursor.KeepAnchor,
1369 1383 len(self._continuation_prompt))
1370 1384 cursor.removeSelectedText()
1371 1385 intercepted = True
1372 1386
1373 1387 # Regular forwards deletion:
1374 1388 else:
1375 1389 anchor = cursor.anchor()
1376 1390 intercepted = (not self._in_buffer(anchor) or
1377 1391 not self._in_buffer(position))
1378 1392
1379 1393 # Don't move the cursor if Control/Cmd is pressed to allow copy-paste
1380 1394 # using the keyboard in any part of the buffer. Also, permit scrolling
1381 1395 # with Page Up/Down keys. Finally, if we're executing, don't move the
1382 1396 # cursor (if even this made sense, we can't guarantee that the prompt
1383 1397 # position is still valid due to text truncation).
1384 1398 if not (self._control_key_down(event.modifiers(), include_command=True)
1385 1399 or key in (QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown)
1386 1400 or (self._executing and not self._reading)):
1387 1401 self._keep_cursor_in_buffer()
1388 1402
1389 1403 return intercepted
1390 1404
1391 1405 def _event_filter_page_keypress(self, event):
1392 1406 """ Filter key events for the paging widget to create console-like
1393 1407 interface.
1394 1408 """
1395 1409 key = event.key()
1396 1410 ctrl_down = self._control_key_down(event.modifiers())
1397 1411 alt_down = event.modifiers() & QtCore.Qt.AltModifier
1398 1412
1399 1413 if ctrl_down:
1400 1414 if key == QtCore.Qt.Key_O:
1401 1415 self._control.setFocus()
1402 1416 intercept = True
1403 1417
1404 1418 elif alt_down:
1405 1419 if key == QtCore.Qt.Key_Greater:
1406 1420 self._page_control.moveCursor(QtGui.QTextCursor.End)
1407 1421 intercepted = True
1408 1422
1409 1423 elif key == QtCore.Qt.Key_Less:
1410 1424 self._page_control.moveCursor(QtGui.QTextCursor.Start)
1411 1425 intercepted = True
1412 1426
1413 1427 elif key in (QtCore.Qt.Key_Q, QtCore.Qt.Key_Escape):
1414 1428 if self._splitter:
1415 1429 self._page_control.hide()
1416 1430 self._control.setFocus()
1417 1431 else:
1418 1432 self.layout().setCurrentWidget(self._control)
1419 1433 return True
1420 1434
1421 1435 elif key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return,
1422 1436 QtCore.Qt.Key_Tab):
1423 1437 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1424 1438 QtCore.Qt.Key_PageDown,
1425 1439 QtCore.Qt.NoModifier)
1426 1440 QtGui.qApp.sendEvent(self._page_control, new_event)
1427 1441 return True
1428 1442
1429 1443 elif key == QtCore.Qt.Key_Backspace:
1430 1444 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1431 1445 QtCore.Qt.Key_PageUp,
1432 1446 QtCore.Qt.NoModifier)
1433 1447 QtGui.qApp.sendEvent(self._page_control, new_event)
1434 1448 return True
1435 1449
1436 1450 return False
1437 1451
1438 1452 def _format_as_columns(self, items, separator=' '):
1439 1453 """ Transform a list of strings into a single string with columns.
1440 1454
1441 1455 Parameters
1442 1456 ----------
1443 1457 items : sequence of strings
1444 1458 The strings to process.
1445 1459
1446 1460 separator : str, optional [default is two spaces]
1447 1461 The string that separates columns.
1448 1462
1449 1463 Returns
1450 1464 -------
1451 1465 The formatted string.
1452 1466 """
1453 1467 # Calculate the number of characters available.
1454 1468 width = self._control.viewport().width()
1455 1469 char_width = QtGui.QFontMetrics(self.font).width(' ')
1456 1470 displaywidth = max(10, (width / char_width) - 1)
1457 1471
1458 1472 return columnize(items, separator, displaywidth)
1459 1473
1460 1474 def _get_block_plain_text(self, block):
1461 1475 """ Given a QTextBlock, return its unformatted text.
1462 1476 """
1463 1477 cursor = QtGui.QTextCursor(block)
1464 1478 cursor.movePosition(QtGui.QTextCursor.StartOfBlock)
1465 1479 cursor.movePosition(QtGui.QTextCursor.EndOfBlock,
1466 1480 QtGui.QTextCursor.KeepAnchor)
1467 1481 return cursor.selection().toPlainText()
1468 1482
1469 1483 def _get_cursor(self):
1470 1484 """ Convenience method that returns a cursor for the current position.
1471 1485 """
1472 1486 return self._control.textCursor()
1473 1487
1474 1488 def _get_end_cursor(self):
1475 1489 """ Convenience method that returns a cursor for the last character.
1476 1490 """
1477 1491 cursor = self._control.textCursor()
1478 1492 cursor.movePosition(QtGui.QTextCursor.End)
1479 1493 return cursor
1480 1494
1481 1495 def _get_input_buffer_cursor_column(self):
1482 1496 """ Returns the column of the cursor in the input buffer, excluding the
1483 1497 contribution by the prompt, or -1 if there is no such column.
1484 1498 """
1485 1499 prompt = self._get_input_buffer_cursor_prompt()
1486 1500 if prompt is None:
1487 1501 return -1
1488 1502 else:
1489 1503 cursor = self._control.textCursor()
1490 1504 return cursor.columnNumber() - len(prompt)
1491 1505
1492 1506 def _get_input_buffer_cursor_line(self):
1493 1507 """ Returns the text of the line of the input buffer that contains the
1494 1508 cursor, or None if there is no such line.
1495 1509 """
1496 1510 prompt = self._get_input_buffer_cursor_prompt()
1497 1511 if prompt is None:
1498 1512 return None
1499 1513 else:
1500 1514 cursor = self._control.textCursor()
1501 1515 text = self._get_block_plain_text(cursor.block())
1502 1516 return text[len(prompt):]
1503 1517
1504 1518 def _get_input_buffer_cursor_prompt(self):
1505 1519 """ Returns the (plain text) prompt for line of the input buffer that
1506 1520 contains the cursor, or None if there is no such line.
1507 1521 """
1508 1522 if self._executing:
1509 1523 return None
1510 1524 cursor = self._control.textCursor()
1511 1525 if cursor.position() >= self._prompt_pos:
1512 1526 if cursor.blockNumber() == self._get_prompt_cursor().blockNumber():
1513 1527 return self._prompt
1514 1528 else:
1515 1529 return self._continuation_prompt
1516 1530 else:
1517 1531 return None
1518 1532
1519 1533 def _get_prompt_cursor(self):
1520 1534 """ Convenience method that returns a cursor for the prompt position.
1521 1535 """
1522 1536 cursor = self._control.textCursor()
1523 1537 cursor.setPosition(self._prompt_pos)
1524 1538 return cursor
1525 1539
1526 1540 def _get_selection_cursor(self, start, end):
1527 1541 """ Convenience method that returns a cursor with text selected between
1528 1542 the positions 'start' and 'end'.
1529 1543 """
1530 1544 cursor = self._control.textCursor()
1531 1545 cursor.setPosition(start)
1532 1546 cursor.setPosition(end, QtGui.QTextCursor.KeepAnchor)
1533 1547 return cursor
1534 1548
1535 1549 def _get_word_start_cursor(self, position):
1536 1550 """ Find the start of the word to the left the given position. If a
1537 1551 sequence of non-word characters precedes the first word, skip over
1538 1552 them. (This emulates the behavior of bash, emacs, etc.)
1539 1553 """
1540 1554 document = self._control.document()
1541 1555 position -= 1
1542 1556 while position >= self._prompt_pos and \
1543 1557 not is_letter_or_number(document.characterAt(position)):
1544 1558 position -= 1
1545 1559 while position >= self._prompt_pos and \
1546 1560 is_letter_or_number(document.characterAt(position)):
1547 1561 position -= 1
1548 1562 cursor = self._control.textCursor()
1549 1563 cursor.setPosition(position + 1)
1550 1564 return cursor
1551 1565
1552 1566 def _get_word_end_cursor(self, position):
1553 1567 """ Find the end of the word to the right the given position. If a
1554 1568 sequence of non-word characters precedes the first word, skip over
1555 1569 them. (This emulates the behavior of bash, emacs, etc.)
1556 1570 """
1557 1571 document = self._control.document()
1558 1572 end = self._get_end_cursor().position()
1559 1573 while position < end and \
1560 1574 not is_letter_or_number(document.characterAt(position)):
1561 1575 position += 1
1562 1576 while position < end and \
1563 1577 is_letter_or_number(document.characterAt(position)):
1564 1578 position += 1
1565 1579 cursor = self._control.textCursor()
1566 1580 cursor.setPosition(position)
1567 1581 return cursor
1568 1582
1569 1583 def _insert_continuation_prompt(self, cursor):
1570 1584 """ Inserts new continuation prompt using the specified cursor.
1571 1585 """
1572 1586 if self._continuation_prompt_html is None:
1573 1587 self._insert_plain_text(cursor, self._continuation_prompt)
1574 1588 else:
1575 1589 self._continuation_prompt = self._insert_html_fetching_plain_text(
1576 1590 cursor, self._continuation_prompt_html)
1577 1591
1578 1592 def _insert_block(self, cursor, block_format=None):
1579 1593 """ Inserts an empty QTextBlock using the specified cursor.
1580 1594 """
1581 1595 if block_format is None:
1582 1596 block_format = QtGui.QTextBlockFormat()
1583 1597 cursor.insertBlock(block_format)
1584 1598
1585 1599 def _insert_html(self, cursor, html):
1586 1600 """ Inserts HTML using the specified cursor in such a way that future
1587 1601 formatting is unaffected.
1588 1602 """
1589 1603 cursor.beginEditBlock()
1590 1604 cursor.insertHtml(html)
1591 1605
1592 1606 # After inserting HTML, the text document "remembers" it's in "html
1593 1607 # mode", which means that subsequent calls adding plain text will result
1594 1608 # in unwanted formatting, lost tab characters, etc. The following code
1595 1609 # hacks around this behavior, which I consider to be a bug in Qt, by
1596 1610 # (crudely) resetting the document's style state.
1597 1611 cursor.movePosition(QtGui.QTextCursor.Left,
1598 1612 QtGui.QTextCursor.KeepAnchor)
1599 1613 if cursor.selection().toPlainText() == ' ':
1600 1614 cursor.removeSelectedText()
1601 1615 else:
1602 1616 cursor.movePosition(QtGui.QTextCursor.Right)
1603 1617 cursor.insertText(' ', QtGui.QTextCharFormat())
1604 1618 cursor.endEditBlock()
1605 1619
1606 1620 def _insert_html_fetching_plain_text(self, cursor, html):
1607 1621 """ Inserts HTML using the specified cursor, then returns its plain text
1608 1622 version.
1609 1623 """
1610 1624 cursor.beginEditBlock()
1611 1625 cursor.removeSelectedText()
1612 1626
1613 1627 start = cursor.position()
1614 1628 self._insert_html(cursor, html)
1615 1629 end = cursor.position()
1616 1630 cursor.setPosition(start, QtGui.QTextCursor.KeepAnchor)
1617 1631 text = cursor.selection().toPlainText()
1618 1632
1619 1633 cursor.setPosition(end)
1620 1634 cursor.endEditBlock()
1621 1635 return text
1622 1636
1623 1637 def _insert_plain_text(self, cursor, text):
1624 1638 """ Inserts plain text using the specified cursor, processing ANSI codes
1625 1639 if enabled.
1626 1640 """
1627 1641 cursor.beginEditBlock()
1628 1642 if self.ansi_codes:
1629 1643 for substring in self._ansi_processor.split_string(text):
1630 1644 for act in self._ansi_processor.actions:
1631 1645
1632 1646 # Unlike real terminal emulators, we don't distinguish
1633 1647 # between the screen and the scrollback buffer. A screen
1634 1648 # erase request clears everything.
1635 1649 if act.action == 'erase' and act.area == 'screen':
1636 1650 cursor.select(QtGui.QTextCursor.Document)
1637 1651 cursor.removeSelectedText()
1638 1652
1639 1653 # Simulate a form feed by scrolling just past the last line.
1640 1654 elif act.action == 'scroll' and act.unit == 'page':
1641 1655 cursor.insertText('\n')
1642 1656 cursor.endEditBlock()
1643 1657 self._set_top_cursor(cursor)
1644 1658 cursor.joinPreviousEditBlock()
1645 1659 cursor.deletePreviousChar()
1646 1660
1647 1661 elif act.action == 'carriage-return':
1648 1662 cursor.movePosition(
1649 1663 cursor.StartOfLine, cursor.KeepAnchor)
1650 1664
1651 1665 elif act.action == 'beep':
1652 1666 QtGui.qApp.beep()
1653 1667
1654 1668 elif act.action == 'backspace':
1655 1669 if not cursor.atBlockStart():
1656 1670 cursor.movePosition(
1657 1671 cursor.PreviousCharacter, cursor.KeepAnchor)
1658 1672
1659 1673 elif act.action == 'newline':
1660 1674 cursor.movePosition(cursor.EndOfLine)
1661 1675
1662 1676 format = self._ansi_processor.get_format()
1663 1677
1664 1678 selection = cursor.selectedText()
1665 1679 if len(selection) == 0:
1666 1680 cursor.insertText(substring, format)
1667 1681 elif substring is not None:
1668 1682 # BS and CR are treated as a change in print
1669 1683 # position, rather than a backwards character
1670 1684 # deletion for output equivalence with (I)Python
1671 1685 # terminal.
1672 1686 if len(substring) >= len(selection):
1673 1687 cursor.insertText(substring, format)
1674 1688 else:
1675 1689 old_text = selection[len(substring):]
1676 1690 cursor.insertText(substring + old_text, format)
1677 1691 cursor.movePosition(cursor.PreviousCharacter,
1678 1692 cursor.KeepAnchor, len(old_text))
1679 1693 else:
1680 1694 cursor.insertText(text)
1681 1695 cursor.endEditBlock()
1682 1696
1683 1697 def _insert_plain_text_into_buffer(self, cursor, text):
1684 1698 """ Inserts text into the input buffer using the specified cursor (which
1685 1699 must be in the input buffer), ensuring that continuation prompts are
1686 1700 inserted as necessary.
1687 1701 """
1688 1702 lines = text.splitlines(True)
1689 1703 if lines:
1690 1704 cursor.beginEditBlock()
1691 1705 cursor.insertText(lines[0])
1692 1706 for line in lines[1:]:
1693 1707 if self._continuation_prompt_html is None:
1694 1708 cursor.insertText(self._continuation_prompt)
1695 1709 else:
1696 1710 self._continuation_prompt = \
1697 1711 self._insert_html_fetching_plain_text(
1698 1712 cursor, self._continuation_prompt_html)
1699 1713 cursor.insertText(line)
1700 1714 cursor.endEditBlock()
1701 1715
1702 1716 def _in_buffer(self, position=None):
1703 1717 """ Returns whether the current cursor (or, if specified, a position) is
1704 1718 inside the editing region.
1705 1719 """
1706 1720 cursor = self._control.textCursor()
1707 1721 if position is None:
1708 1722 position = cursor.position()
1709 1723 else:
1710 1724 cursor.setPosition(position)
1711 1725 line = cursor.blockNumber()
1712 1726 prompt_line = self._get_prompt_cursor().blockNumber()
1713 1727 if line == prompt_line:
1714 1728 return position >= self._prompt_pos
1715 1729 elif line > prompt_line:
1716 1730 cursor.movePosition(QtGui.QTextCursor.StartOfBlock)
1717 1731 prompt_pos = cursor.position() + len(self._continuation_prompt)
1718 1732 return position >= prompt_pos
1719 1733 return False
1720 1734
1721 1735 def _keep_cursor_in_buffer(self):
1722 1736 """ Ensures that the cursor is inside the editing region. Returns
1723 1737 whether the cursor was moved.
1724 1738 """
1725 1739 moved = not self._in_buffer()
1726 1740 if moved:
1727 1741 cursor = self._control.textCursor()
1728 1742 cursor.movePosition(QtGui.QTextCursor.End)
1729 1743 self._control.setTextCursor(cursor)
1730 1744 return moved
1731 1745
1732 1746 def _keyboard_quit(self):
1733 1747 """ Cancels the current editing task ala Ctrl-G in Emacs.
1734 1748 """
1735 1749 if self._temp_buffer_filled :
1736 1750 self._cancel_completion()
1737 1751 self._clear_temporary_buffer()
1738 1752 else:
1739 1753 self.input_buffer = ''
1740 1754
1741 1755 def _page(self, text, html=False):
1742 1756 """ Displays text using the pager if it exceeds the height of the
1743 1757 viewport.
1744 1758
1745 1759 Parameters:
1746 1760 -----------
1747 1761 html : bool, optional (default False)
1748 1762 If set, the text will be interpreted as HTML instead of plain text.
1749 1763 """
1750 1764 line_height = QtGui.QFontMetrics(self.font).height()
1751 1765 minlines = self._control.viewport().height() / line_height
1752 1766 if self.paging != 'none' and \
1753 1767 re.match("(?:[^\n]*\n){%i}" % minlines, text):
1754 1768 if self.paging == 'custom':
1755 1769 self.custom_page_requested.emit(text)
1756 1770 else:
1757 1771 self._page_control.clear()
1758 1772 cursor = self._page_control.textCursor()
1759 1773 if html:
1760 1774 self._insert_html(cursor, text)
1761 1775 else:
1762 1776 self._insert_plain_text(cursor, text)
1763 1777 self._page_control.moveCursor(QtGui.QTextCursor.Start)
1764 1778
1765 1779 self._page_control.viewport().resize(self._control.size())
1766 1780 if self._splitter:
1767 1781 self._page_control.show()
1768 1782 self._page_control.setFocus()
1769 1783 else:
1770 1784 self.layout().setCurrentWidget(self._page_control)
1771 1785 elif html:
1772 1786 self._append_html(text)
1773 1787 else:
1774 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 1816 def _prompt_finished(self):
1777 1817 """ Called immediately after a prompt is finished, i.e. when some input
1778 1818 will be processed and a new prompt displayed.
1779 1819 """
1780 1820 self._control.setReadOnly(True)
1781 1821 self._prompt_finished_hook()
1782 1822
1783 1823 def _prompt_started(self):
1784 1824 """ Called immediately after a new prompt is displayed.
1785 1825 """
1786 1826 # Temporarily disable the maximum block count to permit undo/redo and
1787 1827 # to ensure that the prompt position does not change due to truncation.
1788 1828 self._control.document().setMaximumBlockCount(0)
1789 1829 self._control.setUndoRedoEnabled(True)
1790 1830
1791 1831 # Work around bug in QPlainTextEdit: input method is not re-enabled
1792 1832 # when read-only is disabled.
1793 1833 self._control.setReadOnly(False)
1794 1834 self._control.setAttribute(QtCore.Qt.WA_InputMethodEnabled, True)
1795 1835
1796 1836 if not self._reading:
1797 1837 self._executing = False
1798 1838 self._prompt_started_hook()
1799 1839
1800 1840 # If the input buffer has changed while executing, load it.
1801 1841 if self._input_buffer_pending:
1802 1842 self.input_buffer = self._input_buffer_pending
1803 1843 self._input_buffer_pending = ''
1804 1844
1805 1845 self._control.moveCursor(QtGui.QTextCursor.End)
1806 1846
1807 1847 def _readline(self, prompt='', callback=None):
1808 1848 """ Reads one line of input from the user.
1809 1849
1810 1850 Parameters
1811 1851 ----------
1812 1852 prompt : str, optional
1813 1853 The prompt to print before reading the line.
1814 1854
1815 1855 callback : callable, optional
1816 1856 A callback to execute with the read line. If not specified, input is
1817 1857 read *synchronously* and this method does not return until it has
1818 1858 been read.
1819 1859
1820 1860 Returns
1821 1861 -------
1822 1862 If a callback is specified, returns nothing. Otherwise, returns the
1823 1863 input string with the trailing newline stripped.
1824 1864 """
1825 1865 if self._reading:
1826 1866 raise RuntimeError('Cannot read a line. Widget is already reading.')
1827 1867
1828 1868 if not callback and not self.isVisible():
1829 1869 # If the user cannot see the widget, this function cannot return.
1830 1870 raise RuntimeError('Cannot synchronously read a line if the widget '
1831 1871 'is not visible!')
1832 1872
1833 1873 self._reading = True
1834 1874 self._show_prompt(prompt, newline=False)
1835 1875
1836 1876 if callback is None:
1837 1877 self._reading_callback = None
1838 1878 while self._reading:
1839 1879 QtCore.QCoreApplication.processEvents()
1840 1880 return self._get_input_buffer(force=True).rstrip('\n')
1841 1881
1842 1882 else:
1843 1883 self._reading_callback = lambda: \
1844 1884 callback(self._get_input_buffer(force=True).rstrip('\n'))
1845 1885
1846 1886 def _set_continuation_prompt(self, prompt, html=False):
1847 1887 """ Sets the continuation prompt.
1848 1888
1849 1889 Parameters
1850 1890 ----------
1851 1891 prompt : str
1852 1892 The prompt to show when more input is needed.
1853 1893
1854 1894 html : bool, optional (default False)
1855 1895 If set, the prompt will be inserted as formatted HTML. Otherwise,
1856 1896 the prompt will be treated as plain text, though ANSI color codes
1857 1897 will be handled.
1858 1898 """
1859 1899 if html:
1860 1900 self._continuation_prompt_html = prompt
1861 1901 else:
1862 1902 self._continuation_prompt = prompt
1863 1903 self._continuation_prompt_html = None
1864 1904
1865 1905 def _set_cursor(self, cursor):
1866 1906 """ Convenience method to set the current cursor.
1867 1907 """
1868 1908 self._control.setTextCursor(cursor)
1869 1909
1870 1910 def _set_top_cursor(self, cursor):
1871 1911 """ Scrolls the viewport so that the specified cursor is at the top.
1872 1912 """
1873 1913 scrollbar = self._control.verticalScrollBar()
1874 1914 scrollbar.setValue(scrollbar.maximum())
1875 1915 original_cursor = self._control.textCursor()
1876 1916 self._control.setTextCursor(cursor)
1877 1917 self._control.ensureCursorVisible()
1878 1918 self._control.setTextCursor(original_cursor)
1879 1919
1880 1920 def _show_prompt(self, prompt=None, html=False, newline=True):
1881 1921 """ Writes a new prompt at the end of the buffer.
1882 1922
1883 1923 Parameters
1884 1924 ----------
1885 1925 prompt : str, optional
1886 1926 The prompt to show. If not specified, the previous prompt is used.
1887 1927
1888 1928 html : bool, optional (default False)
1889 1929 Only relevant when a prompt is specified. If set, the prompt will
1890 1930 be inserted as formatted HTML. Otherwise, the prompt will be treated
1891 1931 as plain text, though ANSI color codes will be handled.
1892 1932
1893 1933 newline : bool, optional (default True)
1894 1934 If set, a new line will be written before showing the prompt if
1895 1935 there is not already a newline at the end of the buffer.
1896 1936 """
1897 1937 # Save the current end position to support _append*(before_prompt=True).
1898 1938 cursor = self._get_end_cursor()
1899 1939 self._append_before_prompt_pos = cursor.position()
1900 1940
1901 1941 # Insert a preliminary newline, if necessary.
1902 1942 if newline and cursor.position() > 0:
1903 1943 cursor.movePosition(QtGui.QTextCursor.Left,
1904 1944 QtGui.QTextCursor.KeepAnchor)
1905 1945 if cursor.selection().toPlainText() != '\n':
1906 1946 self._append_block()
1907 1947
1908 1948 # Write the prompt.
1909 1949 self._append_plain_text(self._prompt_sep)
1910 1950 if prompt is None:
1911 1951 if self._prompt_html is None:
1912 1952 self._append_plain_text(self._prompt)
1913 1953 else:
1914 1954 self._append_html(self._prompt_html)
1915 1955 else:
1916 1956 if html:
1917 1957 self._prompt = self._append_html_fetching_plain_text(prompt)
1918 1958 self._prompt_html = prompt
1919 1959 else:
1920 1960 self._append_plain_text(prompt)
1921 1961 self._prompt = prompt
1922 1962 self._prompt_html = None
1923 1963
1924 1964 self._prompt_pos = self._get_end_cursor().position()
1925 1965 self._prompt_started()
1926 1966
1927 1967 #------ Signal handlers ----------------------------------------------------
1928 1968
1929 1969 def _adjust_scrollbars(self):
1930 1970 """ Expands the vertical scrollbar beyond the range set by Qt.
1931 1971 """
1932 1972 # This code is adapted from _q_adjustScrollbars in qplaintextedit.cpp
1933 1973 # and qtextedit.cpp.
1934 1974 document = self._control.document()
1935 1975 scrollbar = self._control.verticalScrollBar()
1936 1976 viewport_height = self._control.viewport().height()
1937 1977 if isinstance(self._control, QtGui.QPlainTextEdit):
1938 1978 maximum = max(0, document.lineCount() - 1)
1939 1979 step = viewport_height / self._control.fontMetrics().lineSpacing()
1940 1980 else:
1941 1981 # QTextEdit does not do line-based layout and blocks will not in
1942 1982 # general have the same height. Therefore it does not make sense to
1943 1983 # attempt to scroll in line height increments.
1944 1984 maximum = document.size().height()
1945 1985 step = viewport_height
1946 1986 diff = maximum - scrollbar.maximum()
1947 1987 scrollbar.setRange(0, maximum)
1948 1988 scrollbar.setPageStep(step)
1949 1989
1950 1990 # Compensate for undesirable scrolling that occurs automatically due to
1951 1991 # maximumBlockCount() text truncation.
1952 1992 if diff < 0 and document.blockCount() == document.maximumBlockCount():
1953 1993 scrollbar.setValue(scrollbar.value() + diff)
1954 1994
1955 1995 def _custom_context_menu_requested(self, pos):
1956 1996 """ Shows a context menu at the given QPoint (in widget coordinates).
1957 1997 """
1958 1998 menu = self._context_menu_make(pos)
1959 1999 menu.exec_(self._control.mapToGlobal(pos))
@@ -1,969 +1,990 b''
1 1 """The Qt MainWindow for the QtConsole
2 2
3 3 This is a tabbed pseudo-terminal of IPython sessions, with a menu bar for
4 4 common actions.
5 5
6 6 Authors:
7 7
8 8 * Evan Patterson
9 9 * Min RK
10 10 * Erik Tollerud
11 11 * Fernando Perez
12 12 * Bussonnier Matthias
13 13 * Thomas Kluyver
14 14 * Paul Ivanov
15 15
16 16 """
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 22 # stdlib imports
23 23 import sys
24 24 import re
25 25 import webbrowser
26 26 import ast
27 27 from threading import Thread
28 28
29 29 # System library imports
30 30 from IPython.external.qt import QtGui,QtCore
31 31
32 32 def background(f):
33 33 """call a function in a simple thread, to prevent blocking"""
34 34 t = Thread(target=f)
35 35 t.start()
36 36 return t
37 37
38 38 #-----------------------------------------------------------------------------
39 39 # Classes
40 40 #-----------------------------------------------------------------------------
41 41
42 42 class MainWindow(QtGui.QMainWindow):
43 43
44 44 #---------------------------------------------------------------------------
45 45 # 'object' interface
46 46 #---------------------------------------------------------------------------
47 47
48 48 _magic_menu_dict = {}
49 49
50 50 def __init__(self, app,
51 51 confirm_exit=True,
52 52 new_frontend_factory=None, slave_frontend_factory=None,
53 53 ):
54 54 """ Create a tabbed MainWindow for managing IPython FrontendWidgets
55 55
56 56 Parameters
57 57 ----------
58 58
59 59 app : reference to QApplication parent
60 60 confirm_exit : bool, optional
61 61 Whether we should prompt on close of tabs
62 62 new_frontend_factory : callable
63 63 A callable that returns a new IPythonWidget instance, attached to
64 64 its own running kernel.
65 65 slave_frontend_factory : callable
66 66 A callable that takes an existing IPythonWidget, and returns a new
67 67 IPythonWidget instance, attached to the same kernel.
68 68 """
69 69
70 70 super(MainWindow, self).__init__()
71 71 self._kernel_counter = 0
72 72 self._app = app
73 73 self.confirm_exit = confirm_exit
74 74 self.new_frontend_factory = new_frontend_factory
75 75 self.slave_frontend_factory = slave_frontend_factory
76 76
77 77 self.tab_widget = QtGui.QTabWidget(self)
78 78 self.tab_widget.setDocumentMode(True)
79 79 self.tab_widget.setTabsClosable(True)
80 80 self.tab_widget.tabCloseRequested[int].connect(self.close_tab)
81 81
82 82 self.setCentralWidget(self.tab_widget)
83 83 # hide tab bar at first, since we have no tabs:
84 84 self.tab_widget.tabBar().setVisible(False)
85 85 # prevent focus in tab bar
86 86 self.tab_widget.setFocusPolicy(QtCore.Qt.NoFocus)
87 87
88 88 def update_tab_bar_visibility(self):
89 89 """ update visibility of the tabBar depending of the number of tab
90 90
91 91 0 or 1 tab, tabBar hidden
92 92 2+ tabs, tabBar visible
93 93
94 94 send a self.close if number of tab ==0
95 95
96 96 need to be called explicitly, or be connected to tabInserted/tabRemoved
97 97 """
98 98 if self.tab_widget.count() <= 1:
99 99 self.tab_widget.tabBar().setVisible(False)
100 100 else:
101 101 self.tab_widget.tabBar().setVisible(True)
102 102 if self.tab_widget.count()==0 :
103 103 self.close()
104 104
105 105 @property
106 106 def next_kernel_id(self):
107 107 """constantly increasing counter for kernel IDs"""
108 108 c = self._kernel_counter
109 109 self._kernel_counter += 1
110 110 return c
111 111
112 112 @property
113 113 def active_frontend(self):
114 114 return self.tab_widget.currentWidget()
115 115
116 116 def create_tab_with_new_frontend(self):
117 117 """create a new frontend and attach it to a new tab"""
118 118 widget = self.new_frontend_factory()
119 119 self.add_tab_with_frontend(widget)
120 120
121 121 def create_tab_with_current_kernel(self):
122 122 """create a new frontend attached to the same kernel as the current tab"""
123 123 current_widget = self.tab_widget.currentWidget()
124 124 current_widget_index = self.tab_widget.indexOf(current_widget)
125 125 current_widget_name = self.tab_widget.tabText(current_widget_index)
126 126 widget = self.slave_frontend_factory(current_widget)
127 127 if 'slave' in current_widget_name:
128 128 # don't keep stacking slaves
129 129 name = current_widget_name
130 130 else:
131 131 name = '(%s) slave' % current_widget_name
132 132 self.add_tab_with_frontend(widget,name=name)
133 133
134 134 def close_tab(self,current_tab):
135 135 """ Called when you need to try to close a tab.
136 136
137 137 It takes the number of the tab to be closed as argument, or a reference
138 138 to the widget inside this tab
139 139 """
140 140
141 141 # let's be sure "tab" and "closing widget" are respectively the index
142 142 # of the tab to close and a reference to the frontend to close
143 143 if type(current_tab) is not int :
144 144 current_tab = self.tab_widget.indexOf(current_tab)
145 145 closing_widget=self.tab_widget.widget(current_tab)
146 146
147 147
148 148 # when trying to be closed, widget might re-send a request to be
149 149 # closed again, but will be deleted when event will be processed. So
150 150 # need to check that widget still exists and skip if not. One example
151 151 # of this is when 'exit' is sent in a slave tab. 'exit' will be
152 152 # re-sent by this function on the master widget, which ask all slave
153 153 # widgets to exit
154 154 if closing_widget==None:
155 155 return
156 156
157 157 #get a list of all slave widgets on the same kernel.
158 158 slave_tabs = self.find_slave_widgets(closing_widget)
159 159
160 160 keepkernel = None #Use the prompt by default
161 161 if hasattr(closing_widget,'_keep_kernel_on_exit'): #set by exit magic
162 162 keepkernel = closing_widget._keep_kernel_on_exit
163 163 # If signal sent by exit magic (_keep_kernel_on_exit, exist and not None)
164 164 # we set local slave tabs._hidden to True to avoid prompting for kernel
165 165 # restart when they get the signal. and then "forward" the 'exit'
166 166 # to the main window
167 167 if keepkernel is not None:
168 168 for tab in slave_tabs:
169 169 tab._hidden = True
170 170 if closing_widget in slave_tabs:
171 171 try :
172 172 self.find_master_tab(closing_widget).execute('exit')
173 173 except AttributeError:
174 174 self.log.info("Master already closed or not local, closing only current tab")
175 175 self.tab_widget.removeTab(current_tab)
176 176 self.update_tab_bar_visibility()
177 177 return
178 178
179 179 kernel_manager = closing_widget.kernel_manager
180 180
181 181 if keepkernel is None and not closing_widget._confirm_exit:
182 182 # don't prompt, just terminate the kernel if we own it
183 183 # or leave it alone if we don't
184 184 keepkernel = closing_widget._existing
185 185 if keepkernel is None: #show prompt
186 186 if kernel_manager and kernel_manager.channels_running:
187 187 title = self.window().windowTitle()
188 188 cancel = QtGui.QMessageBox.Cancel
189 189 okay = QtGui.QMessageBox.Ok
190 190 if closing_widget._may_close:
191 191 msg = "You are closing the tab : "+'"'+self.tab_widget.tabText(current_tab)+'"'
192 192 info = "Would you like to quit the Kernel and close all attached Consoles as well?"
193 193 justthis = QtGui.QPushButton("&No, just this Tab", self)
194 194 justthis.setShortcut('N')
195 195 closeall = QtGui.QPushButton("&Yes, close all", self)
196 196 closeall.setShortcut('Y')
197 197 # allow ctrl-d ctrl-d exit, like in terminal
198 198 closeall.setShortcut('Ctrl+D')
199 199 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
200 200 title, msg)
201 201 box.setInformativeText(info)
202 202 box.addButton(cancel)
203 203 box.addButton(justthis, QtGui.QMessageBox.NoRole)
204 204 box.addButton(closeall, QtGui.QMessageBox.YesRole)
205 205 box.setDefaultButton(closeall)
206 206 box.setEscapeButton(cancel)
207 207 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
208 208 box.setIconPixmap(pixmap)
209 209 reply = box.exec_()
210 210 if reply == 1: # close All
211 211 for slave in slave_tabs:
212 212 background(slave.kernel_manager.stop_channels)
213 213 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
214 214 closing_widget.execute("exit")
215 215 self.tab_widget.removeTab(current_tab)
216 216 background(kernel_manager.stop_channels)
217 217 elif reply == 0: # close Console
218 218 if not closing_widget._existing:
219 219 # Have kernel: don't quit, just close the tab
220 220 closing_widget.execute("exit True")
221 221 self.tab_widget.removeTab(current_tab)
222 222 background(kernel_manager.stop_channels)
223 223 else:
224 224 reply = QtGui.QMessageBox.question(self, title,
225 225 "Are you sure you want to close this Console?"+
226 226 "\nThe Kernel and other Consoles will remain active.",
227 227 okay|cancel,
228 228 defaultButton=okay
229 229 )
230 230 if reply == okay:
231 231 self.tab_widget.removeTab(current_tab)
232 232 elif keepkernel: #close console but leave kernel running (no prompt)
233 233 self.tab_widget.removeTab(current_tab)
234 234 background(kernel_manager.stop_channels)
235 235 else: #close console and kernel (no prompt)
236 236 self.tab_widget.removeTab(current_tab)
237 237 if kernel_manager and kernel_manager.channels_running:
238 238 for slave in slave_tabs:
239 239 background(slave.kernel_manager.stop_channels)
240 240 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
241 241 kernel_manager.shutdown_kernel()
242 242 background(kernel_manager.stop_channels)
243 243
244 244 self.update_tab_bar_visibility()
245 245
246 246 def add_tab_with_frontend(self,frontend,name=None):
247 247 """ insert a tab with a given frontend in the tab bar, and give it a name
248 248
249 249 """
250 250 if not name:
251 251 name = 'kernel %i' % self.next_kernel_id
252 252 self.tab_widget.addTab(frontend,name)
253 253 self.update_tab_bar_visibility()
254 254 self.make_frontend_visible(frontend)
255 255 frontend.exit_requested.connect(self.close_tab)
256 256
257 257 def next_tab(self):
258 258 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()+1))
259 259
260 260 def prev_tab(self):
261 261 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()-1))
262 262
263 263 def make_frontend_visible(self,frontend):
264 264 widget_index=self.tab_widget.indexOf(frontend)
265 265 if widget_index > 0 :
266 266 self.tab_widget.setCurrentIndex(widget_index)
267 267
268 268 def find_master_tab(self,tab,as_list=False):
269 269 """
270 270 Try to return the frontend that owns the kernel attached to the given widget/tab.
271 271
272 272 Only finds frontend owned by the current application. Selection
273 273 based on port of the kernel might be inaccurate if several kernel
274 274 on different ip use same port number.
275 275
276 276 This function does the conversion tabNumber/widget if needed.
277 277 Might return None if no master widget (non local kernel)
278 278 Will crash IPython if more than 1 masterWidget
279 279
280 280 When asList set to True, always return a list of widget(s) owning
281 281 the kernel. The list might be empty or containing several Widget.
282 282 """
283 283
284 284 #convert from/to int/richIpythonWidget if needed
285 285 if isinstance(tab, int):
286 286 tab = self.tab_widget.widget(tab)
287 287 km=tab.kernel_manager
288 288
289 289 #build list of all widgets
290 290 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
291 291
292 292 # widget that are candidate to be the owner of the kernel does have all the same port of the curent widget
293 293 # And should have a _may_close attribute
294 294 filtered_widget_list = [ widget for widget in widget_list if
295 295 widget.kernel_manager.connection_file == km.connection_file and
296 296 hasattr(widget,'_may_close') ]
297 297 # the master widget is the one that may close the kernel
298 298 master_widget= [ widget for widget in filtered_widget_list if widget._may_close]
299 299 if as_list:
300 300 return master_widget
301 301 assert(len(master_widget)<=1 )
302 302 if len(master_widget)==0:
303 303 return None
304 304
305 305 return master_widget[0]
306 306
307 307 def find_slave_widgets(self,tab):
308 308 """return all the frontends that do not own the kernel attached to the given widget/tab.
309 309
310 310 Only find frontends owned by the current application. Selection
311 311 based on connection file of the kernel.
312 312
313 313 This function does the conversion tabNumber/widget if needed.
314 314 """
315 315 #convert from/to int/richIpythonWidget if needed
316 316 if isinstance(tab, int):
317 317 tab = self.tab_widget.widget(tab)
318 318 km=tab.kernel_manager
319 319
320 320 #build list of all widgets
321 321 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
322 322
323 323 # widget that are candidate not to be the owner of the kernel does have all the same port of the curent widget
324 324 filtered_widget_list = ( widget for widget in widget_list if
325 325 widget.kernel_manager.connection_file == km.connection_file)
326 326 # Get a list of all widget owning the same kernel and removed it from
327 327 # the previous cadidate. (better using sets ?)
328 328 master_widget_list = self.find_master_tab(tab, as_list=True)
329 329 slave_list = [widget for widget in filtered_widget_list if widget not in master_widget_list]
330 330
331 331 return slave_list
332 332
333 333 # Populate the menu bar with common actions and shortcuts
334 334 def add_menu_action(self, menu, action, defer_shortcut=False):
335 335 """Add action to menu as well as self
336 336
337 337 So that when the menu bar is invisible, its actions are still available.
338 338
339 339 If defer_shortcut is True, set the shortcut context to widget-only,
340 340 where it will avoid conflict with shortcuts already bound to the
341 341 widgets themselves.
342 342 """
343 343 menu.addAction(action)
344 344 self.addAction(action)
345 345
346 346 if defer_shortcut:
347 347 action.setShortcutContext(QtCore.Qt.WidgetShortcut)
348 348
349 349 def init_menu_bar(self):
350 350 #create menu in the order they should appear in the menu bar
351 351 self.init_file_menu()
352 352 self.init_edit_menu()
353 353 self.init_view_menu()
354 354 self.init_kernel_menu()
355 355 self.init_magic_menu()
356 356 self.init_window_menu()
357 357 self.init_help_menu()
358 358
359 359 def init_file_menu(self):
360 360 self.file_menu = self.menuBar().addMenu("&File")
361 361
362 362 self.new_kernel_tab_act = QtGui.QAction("New Tab with &New kernel",
363 363 self,
364 364 shortcut="Ctrl+T",
365 365 triggered=self.create_tab_with_new_frontend)
366 366 self.add_menu_action(self.file_menu, self.new_kernel_tab_act)
367 367
368 368 self.slave_kernel_tab_act = QtGui.QAction("New Tab with Sa&me kernel",
369 369 self,
370 370 shortcut="Ctrl+Shift+T",
371 371 triggered=self.create_tab_with_current_kernel)
372 372 self.add_menu_action(self.file_menu, self.slave_kernel_tab_act)
373 373
374 374 self.file_menu.addSeparator()
375 375
376 376 self.close_action=QtGui.QAction("&Close Tab",
377 377 self,
378 378 shortcut=QtGui.QKeySequence.Close,
379 379 triggered=self.close_active_frontend
380 380 )
381 381 self.add_menu_action(self.file_menu, self.close_action)
382 382
383 383 self.export_action=QtGui.QAction("&Save to HTML/XHTML",
384 384 self,
385 385 shortcut=QtGui.QKeySequence.Save,
386 386 triggered=self.export_action_active_frontend
387 387 )
388 388 self.add_menu_action(self.file_menu, self.export_action, True)
389 389
390 390 self.file_menu.addSeparator()
391 391
392 392 printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print)
393 393 if printkey.matches("Ctrl+P") and sys.platform != 'darwin':
394 394 # Only override the default if there is a collision.
395 395 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
396 396 printkey = "Ctrl+Shift+P"
397 397 self.print_action = QtGui.QAction("&Print",
398 398 self,
399 399 shortcut=printkey,
400 400 triggered=self.print_action_active_frontend)
401 401 self.add_menu_action(self.file_menu, self.print_action, True)
402 402
403 403 if sys.platform != 'darwin':
404 404 # OSX always has Quit in the Application menu, only add it
405 405 # to the File menu elsewhere.
406 406
407 407 self.file_menu.addSeparator()
408 408
409 409 self.quit_action = QtGui.QAction("&Quit",
410 410 self,
411 411 shortcut=QtGui.QKeySequence.Quit,
412 412 triggered=self.close,
413 413 )
414 414 self.add_menu_action(self.file_menu, self.quit_action)
415 415
416 416
417 417 def init_edit_menu(self):
418 418 self.edit_menu = self.menuBar().addMenu("&Edit")
419 419
420 420 self.undo_action = QtGui.QAction("&Undo",
421 421 self,
422 422 shortcut=QtGui.QKeySequence.Undo,
423 423 statusTip="Undo last action if possible",
424 424 triggered=self.undo_active_frontend
425 425 )
426 426 self.add_menu_action(self.edit_menu, self.undo_action)
427 427
428 428 self.redo_action = QtGui.QAction("&Redo",
429 429 self,
430 430 shortcut=QtGui.QKeySequence.Redo,
431 431 statusTip="Redo last action if possible",
432 432 triggered=self.redo_active_frontend)
433 433 self.add_menu_action(self.edit_menu, self.redo_action)
434 434
435 435 self.edit_menu.addSeparator()
436 436
437 437 self.cut_action = QtGui.QAction("&Cut",
438 438 self,
439 439 shortcut=QtGui.QKeySequence.Cut,
440 440 triggered=self.cut_active_frontend
441 441 )
442 442 self.add_menu_action(self.edit_menu, self.cut_action, True)
443 443
444 444 self.copy_action = QtGui.QAction("&Copy",
445 445 self,
446 446 shortcut=QtGui.QKeySequence.Copy,
447 447 triggered=self.copy_active_frontend
448 448 )
449 449 self.add_menu_action(self.edit_menu, self.copy_action, True)
450 450
451 451 self.copy_raw_action = QtGui.QAction("Copy (&Raw Text)",
452 452 self,
453 453 shortcut="Ctrl+Shift+C",
454 454 triggered=self.copy_raw_active_frontend
455 455 )
456 456 self.add_menu_action(self.edit_menu, self.copy_raw_action, True)
457 457
458 458 self.paste_action = QtGui.QAction("&Paste",
459 459 self,
460 460 shortcut=QtGui.QKeySequence.Paste,
461 461 triggered=self.paste_active_frontend
462 462 )
463 463 self.add_menu_action(self.edit_menu, self.paste_action, True)
464 464
465 465 self.edit_menu.addSeparator()
466 466
467 467 selectall = QtGui.QKeySequence(QtGui.QKeySequence.SelectAll)
468 468 if selectall.matches("Ctrl+A") and sys.platform != 'darwin':
469 469 # Only override the default if there is a collision.
470 470 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
471 471 selectall = "Ctrl+Shift+A"
472 472 self.select_all_action = QtGui.QAction("Select &All",
473 473 self,
474 474 shortcut=selectall,
475 475 triggered=self.select_all_active_frontend
476 476 )
477 477 self.add_menu_action(self.edit_menu, self.select_all_action, True)
478 478
479 479
480 480 def init_view_menu(self):
481 481 self.view_menu = self.menuBar().addMenu("&View")
482 482
483 483 if sys.platform != 'darwin':
484 484 # disable on OSX, where there is always a menu bar
485 485 self.toggle_menu_bar_act = QtGui.QAction("Toggle &Menu Bar",
486 486 self,
487 487 shortcut="Ctrl+Shift+M",
488 488 statusTip="Toggle visibility of menubar",
489 489 triggered=self.toggle_menu_bar)
490 490 self.add_menu_action(self.view_menu, self.toggle_menu_bar_act)
491 491
492 492 fs_key = "Ctrl+Meta+F" if sys.platform == 'darwin' else "F11"
493 493 self.full_screen_act = QtGui.QAction("&Full Screen",
494 494 self,
495 495 shortcut=fs_key,
496 496 statusTip="Toggle between Fullscreen and Normal Size",
497 497 triggered=self.toggleFullScreen)
498 498 self.add_menu_action(self.view_menu, self.full_screen_act)
499 499
500 500 self.view_menu.addSeparator()
501 501
502 502 self.increase_font_size = QtGui.QAction("Zoom &In",
503 503 self,
504 504 shortcut=QtGui.QKeySequence.ZoomIn,
505 505 triggered=self.increase_font_size_active_frontend
506 506 )
507 507 self.add_menu_action(self.view_menu, self.increase_font_size, True)
508 508
509 509 self.decrease_font_size = QtGui.QAction("Zoom &Out",
510 510 self,
511 511 shortcut=QtGui.QKeySequence.ZoomOut,
512 512 triggered=self.decrease_font_size_active_frontend
513 513 )
514 514 self.add_menu_action(self.view_menu, self.decrease_font_size, True)
515 515
516 516 self.reset_font_size = QtGui.QAction("Zoom &Reset",
517 517 self,
518 518 shortcut="Ctrl+0",
519 519 triggered=self.reset_font_size_active_frontend
520 520 )
521 521 self.add_menu_action(self.view_menu, self.reset_font_size, True)
522 522
523 523 self.view_menu.addSeparator()
524 524
525 525 self.clear_action = QtGui.QAction("&Clear Screen",
526 526 self,
527 527 shortcut='Ctrl+L',
528 528 statusTip="Clear the console",
529 529 triggered=self.clear_magic_active_frontend)
530 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 550 def init_kernel_menu(self):
533 551 self.kernel_menu = self.menuBar().addMenu("&Kernel")
534 552 # Qt on OSX maps Ctrl to Cmd, and Meta to Ctrl
535 553 # keep the signal shortcuts to ctrl, rather than
536 554 # platform-default like we do elsewhere.
537 555
538 556 ctrl = "Meta" if sys.platform == 'darwin' else "Ctrl"
539 557
540 558 self.interrupt_kernel_action = QtGui.QAction("&Interrupt current Kernel",
541 559 self,
542 560 triggered=self.interrupt_kernel_active_frontend,
543 561 shortcut=ctrl+"+C",
544 562 )
545 563 self.add_menu_action(self.kernel_menu, self.interrupt_kernel_action)
546 564
547 565 self.restart_kernel_action = QtGui.QAction("&Restart current Kernel",
548 566 self,
549 567 triggered=self.restart_kernel_active_frontend,
550 568 shortcut=ctrl+"+.",
551 569 )
552 570 self.add_menu_action(self.kernel_menu, self.restart_kernel_action)
553 571
554 572 self.kernel_menu.addSeparator()
555 573
556 574 self.confirm_restart_kernel_action = QtGui.QAction("&Confirm kernel restart",
557 575 self,
558 576 checkable=True,
559 577 checked=self.active_frontend.confirm_restart,
560 578 triggered=self.toggle_confirm_restart_active_frontend
561 579 )
562 580
563 581 self.add_menu_action(self.kernel_menu, self.confirm_restart_kernel_action)
564 582 self.tab_widget.currentChanged.connect(self.update_restart_checkbox)
565 583
566 584 def _make_dynamic_magic(self,magic):
567 585 """Return a function `fun` that will execute `magic` on active frontend.
568 586
569 587 Parameters
570 588 ----------
571 589 magic : string
572 590 string that will be executed as is when the returned function is called
573 591
574 592 Returns
575 593 -------
576 594 fun : function
577 595 function with no parameters, when called will execute `magic` on the
578 596 current active frontend at call time
579 597
580 598 See Also
581 599 --------
582 600 populate_all_magic_menu : generate the "All Magics..." menu
583 601
584 602 Notes
585 603 -----
586 604 `fun` executes `magic` in active frontend at the moment it is triggered,
587 605 not the active frontend at the moment it was created.
588 606
589 607 This function is mostly used to create the "All Magics..." Menu at run time.
590 608 """
591 609 # need two level nested function to be sure to pass magic
592 610 # to active frontend **at run time**.
593 611 def inner_dynamic_magic():
594 612 self.active_frontend.execute(magic)
595 613 inner_dynamic_magic.__name__ = "dynamics_magic_s"
596 614 return inner_dynamic_magic
597 615
598 616 def populate_all_magic_menu(self, listofmagic=None):
599 617 """Clean "All Magics..." menu and repopulate it with `listofmagic`
600 618
601 619 Parameters
602 620 ----------
603 621 listofmagic : string,
604 622 repr() of a list of strings, send back by the kernel
605 623
606 624 Notes
607 625 -----
608 626 `listofmagic`is a repr() of list because it is fed with the result of
609 627 a 'user_expression'
610 628 """
611 629 for k,v in self._magic_menu_dict.items():
612 630 v.clear()
613 631 self.all_magic_menu.clear()
614 632
615 633
616 634 mlist=ast.literal_eval(listofmagic)
617 635 for magic in mlist:
618 636 cell = (magic['type'] == 'cell')
619 637 name = magic['name']
620 638 mclass = magic['class']
621 639 if cell :
622 640 prefix='%%'
623 641 else :
624 642 prefix='%'
625 643 magic_menu = self._get_magic_menu(mclass)
626 644
627 645 pmagic = '%s%s'%(prefix,name)
628 646
629 647 xaction = QtGui.QAction(pmagic,
630 648 self,
631 649 triggered=self._make_dynamic_magic(pmagic)
632 650 )
633 651 magic_menu.addAction(xaction)
634 652 self.all_magic_menu.addAction(xaction)
635 653
636 654 def update_all_magic_menu(self):
637 655 """ Update the list of magics in the "All Magics..." Menu
638 656
639 657 Request the kernel with the list of available magics and populate the
640 658 menu with the list received back
641 659
642 660 """
643 661 self.active_frontend._silent_exec_callback('get_ipython().magics_manager.lsmagic_info()',
644 662 self.populate_all_magic_menu)
645 663
646 664 def _get_magic_menu(self,menuidentifier, menulabel=None):
647 665 """return a submagic menu by name, and create it if needed
648 666
649 667 parameters:
650 668 -----------
651 669
652 670 menulabel : str
653 671 Label for the menu
654 672
655 673 Will infere the menu name from the identifier at creation if menulabel not given.
656 674 To do so you have too give menuidentifier as a CamelCassedString
657 675 """
658 676 menu = self._magic_menu_dict.get(menuidentifier,None)
659 677 if not menu :
660 678 if not menulabel:
661 679 menulabel = re.sub("([a-zA-Z]+)([A-Z][a-z])","\g<1> \g<2>",menuidentifier)
662 680 menu = QtGui.QMenu(menulabel,self.magic_menu)
663 681 self._magic_menu_dict[menuidentifier]=menu
664 682 self.magic_menu.insertMenu(self.magic_menu_separator,menu)
665 683 return menu
666 684
667 685
668 686
669 687 def init_magic_menu(self):
670 688 self.magic_menu = self.menuBar().addMenu("&Magic")
671 689 self.magic_menu_separator = self.magic_menu.addSeparator()
672 690
673 691 self.all_magic_menu = self._get_magic_menu("AllMagics", menulabel="&All Magics...")
674 692
675 693 # This action should usually not appear as it will be cleared when menu
676 694 # is updated at first kernel response. Though, it is necessary when
677 695 # connecting through X-forwarding, as in this case, the menu is not
678 696 # auto updated, SO DO NOT DELETE.
679 697 self.pop = QtGui.QAction("&Update All Magic Menu ",
680 698 self, triggered=self.update_all_magic_menu)
681 699 self.add_menu_action(self.all_magic_menu, self.pop)
682 700 # we need to populate the 'Magic Menu' once the kernel has answer at
683 701 # least once let's do it immediately, but it's assured to works
684 702 self.pop.trigger()
685 703
686 704 self.reset_action = QtGui.QAction("&Reset",
687 705 self,
688 706 statusTip="Clear all variables from workspace",
689 707 triggered=self.reset_magic_active_frontend)
690 708 self.add_menu_action(self.magic_menu, self.reset_action)
691 709
692 710 self.history_action = QtGui.QAction("&History",
693 711 self,
694 712 statusTip="show command history",
695 713 triggered=self.history_magic_active_frontend)
696 714 self.add_menu_action(self.magic_menu, self.history_action)
697 715
698 716 self.save_action = QtGui.QAction("E&xport History ",
699 717 self,
700 718 statusTip="Export History as Python File",
701 719 triggered=self.save_magic_active_frontend)
702 720 self.add_menu_action(self.magic_menu, self.save_action)
703 721
704 722 self.who_action = QtGui.QAction("&Who",
705 723 self,
706 724 statusTip="List interactive variables",
707 725 triggered=self.who_magic_active_frontend)
708 726 self.add_menu_action(self.magic_menu, self.who_action)
709 727
710 728 self.who_ls_action = QtGui.QAction("Wh&o ls",
711 729 self,
712 730 statusTip="Return a list of interactive variables",
713 731 triggered=self.who_ls_magic_active_frontend)
714 732 self.add_menu_action(self.magic_menu, self.who_ls_action)
715 733
716 734 self.whos_action = QtGui.QAction("Who&s",
717 735 self,
718 736 statusTip="List interactive variables with details",
719 737 triggered=self.whos_magic_active_frontend)
720 738 self.add_menu_action(self.magic_menu, self.whos_action)
721 739
722 740 def init_window_menu(self):
723 741 self.window_menu = self.menuBar().addMenu("&Window")
724 742 if sys.platform == 'darwin':
725 743 # add min/maximize actions to OSX, which lacks default bindings.
726 744 self.minimizeAct = QtGui.QAction("Mini&mize",
727 745 self,
728 746 shortcut="Ctrl+m",
729 747 statusTip="Minimize the window/Restore Normal Size",
730 748 triggered=self.toggleMinimized)
731 749 # maximize is called 'Zoom' on OSX for some reason
732 750 self.maximizeAct = QtGui.QAction("&Zoom",
733 751 self,
734 752 shortcut="Ctrl+Shift+M",
735 753 statusTip="Maximize the window/Restore Normal Size",
736 754 triggered=self.toggleMaximized)
737 755
738 756 self.add_menu_action(self.window_menu, self.minimizeAct)
739 757 self.add_menu_action(self.window_menu, self.maximizeAct)
740 758 self.window_menu.addSeparator()
741 759
742 760 prev_key = "Ctrl+Shift+Left" if sys.platform == 'darwin' else "Ctrl+PgUp"
743 761 self.prev_tab_act = QtGui.QAction("Pre&vious Tab",
744 762 self,
745 763 shortcut=prev_key,
746 764 statusTip="Select previous tab",
747 765 triggered=self.prev_tab)
748 766 self.add_menu_action(self.window_menu, self.prev_tab_act)
749 767
750 768 next_key = "Ctrl+Shift+Right" if sys.platform == 'darwin' else "Ctrl+PgDown"
751 769 self.next_tab_act = QtGui.QAction("Ne&xt Tab",
752 770 self,
753 771 shortcut=next_key,
754 772 statusTip="Select next tab",
755 773 triggered=self.next_tab)
756 774 self.add_menu_action(self.window_menu, self.next_tab_act)
757 775
758 776 def init_help_menu(self):
759 777 # please keep the Help menu in Mac Os even if empty. It will
760 778 # automatically contain a search field to search inside menus and
761 779 # please keep it spelled in English, as long as Qt Doesn't support
762 780 # a QAction.MenuRole like HelpMenuRole otherwise it will lose
763 781 # this search field functionality
764 782
765 783 self.help_menu = self.menuBar().addMenu("&Help")
766 784
767 785
768 786 # Help Menu
769 787
770 788 self.intro_active_frontend_action = QtGui.QAction("&Intro to IPython",
771 789 self,
772 790 triggered=self.intro_active_frontend
773 791 )
774 792 self.add_menu_action(self.help_menu, self.intro_active_frontend_action)
775 793
776 794 self.quickref_active_frontend_action = QtGui.QAction("IPython &Cheat Sheet",
777 795 self,
778 796 triggered=self.quickref_active_frontend
779 797 )
780 798 self.add_menu_action(self.help_menu, self.quickref_active_frontend_action)
781 799
782 800 self.guiref_active_frontend_action = QtGui.QAction("&Qt Console",
783 801 self,
784 802 triggered=self.guiref_active_frontend
785 803 )
786 804 self.add_menu_action(self.help_menu, self.guiref_active_frontend_action)
787 805
788 806 self.onlineHelpAct = QtGui.QAction("Open Online &Help",
789 807 self,
790 808 triggered=self._open_online_help)
791 809 self.add_menu_action(self.help_menu, self.onlineHelpAct)
792 810
793 811 # minimize/maximize/fullscreen actions:
794 812
795 813 def toggle_menu_bar(self):
796 814 menu_bar = self.menuBar()
797 815 if menu_bar.isVisible():
798 816 menu_bar.setVisible(False)
799 817 else:
800 818 menu_bar.setVisible(True)
801 819
802 820 def toggleMinimized(self):
803 821 if not self.isMinimized():
804 822 self.showMinimized()
805 823 else:
806 824 self.showNormal()
807 825
808 826 def _open_online_help(self):
809 827 filename="http://ipython.org/ipython-doc/stable/index.html"
810 828 webbrowser.open(filename, new=1, autoraise=True)
811 829
812 830 def toggleMaximized(self):
813 831 if not self.isMaximized():
814 832 self.showMaximized()
815 833 else:
816 834 self.showNormal()
817 835
818 836 # Min/Max imizing while in full screen give a bug
819 837 # when going out of full screen, at least on OSX
820 838 def toggleFullScreen(self):
821 839 if not self.isFullScreen():
822 840 self.showFullScreen()
823 841 if sys.platform == 'darwin':
824 842 self.maximizeAct.setEnabled(False)
825 843 self.minimizeAct.setEnabled(False)
826 844 else:
827 845 self.showNormal()
828 846 if sys.platform == 'darwin':
829 847 self.maximizeAct.setEnabled(True)
830 848 self.minimizeAct.setEnabled(True)
831 849
850 def set_paging_active_frontend(self, paging):
851 self.active_frontend._set_paging(paging)
852
832 853 def close_active_frontend(self):
833 854 self.close_tab(self.active_frontend)
834 855
835 856 def restart_kernel_active_frontend(self):
836 857 self.active_frontend.request_restart_kernel()
837 858
838 859 def interrupt_kernel_active_frontend(self):
839 860 self.active_frontend.request_interrupt_kernel()
840 861
841 862 def toggle_confirm_restart_active_frontend(self):
842 863 widget = self.active_frontend
843 864 widget.confirm_restart = not widget.confirm_restart
844 865 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
845 866
846 867 def update_restart_checkbox(self):
847 868 if self.active_frontend is None:
848 869 return
849 870 widget = self.active_frontend
850 871 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
851 872
852 873 def cut_active_frontend(self):
853 874 widget = self.active_frontend
854 875 if widget.can_cut():
855 876 widget.cut()
856 877
857 878 def copy_active_frontend(self):
858 879 widget = self.active_frontend
859 880 widget.copy()
860 881
861 882 def copy_raw_active_frontend(self):
862 883 self.active_frontend._copy_raw_action.trigger()
863 884
864 885 def paste_active_frontend(self):
865 886 widget = self.active_frontend
866 887 if widget.can_paste():
867 888 widget.paste()
868 889
869 890 def undo_active_frontend(self):
870 891 self.active_frontend.undo()
871 892
872 893 def redo_active_frontend(self):
873 894 self.active_frontend.redo()
874 895
875 896 def reset_magic_active_frontend(self):
876 897 self.active_frontend.execute("%reset")
877 898
878 899 def history_magic_active_frontend(self):
879 900 self.active_frontend.execute("%history")
880 901
881 902 def save_magic_active_frontend(self):
882 903 self.active_frontend.save_magic()
883 904
884 905 def clear_magic_active_frontend(self):
885 906 self.active_frontend.execute("%clear")
886 907
887 908 def who_magic_active_frontend(self):
888 909 self.active_frontend.execute("%who")
889 910
890 911 def who_ls_magic_active_frontend(self):
891 912 self.active_frontend.execute("%who_ls")
892 913
893 914 def whos_magic_active_frontend(self):
894 915 self.active_frontend.execute("%whos")
895 916
896 917 def print_action_active_frontend(self):
897 918 self.active_frontend.print_action.trigger()
898 919
899 920 def export_action_active_frontend(self):
900 921 self.active_frontend.export_action.trigger()
901 922
902 923 def select_all_active_frontend(self):
903 924 self.active_frontend.select_all_action.trigger()
904 925
905 926 def increase_font_size_active_frontend(self):
906 927 self.active_frontend.increase_font_size.trigger()
907 928
908 929 def decrease_font_size_active_frontend(self):
909 930 self.active_frontend.decrease_font_size.trigger()
910 931
911 932 def reset_font_size_active_frontend(self):
912 933 self.active_frontend.reset_font_size.trigger()
913 934
914 935 def guiref_active_frontend(self):
915 936 self.active_frontend.execute("%guiref")
916 937
917 938 def intro_active_frontend(self):
918 939 self.active_frontend.execute("?")
919 940
920 941 def quickref_active_frontend(self):
921 942 self.active_frontend.execute("%quickref")
922 943 #---------------------------------------------------------------------------
923 944 # QWidget interface
924 945 #---------------------------------------------------------------------------
925 946
926 947 def closeEvent(self, event):
927 948 """ Forward the close event to every tabs contained by the windows
928 949 """
929 950 if self.tab_widget.count() == 0:
930 951 # no tabs, just close
931 952 event.accept()
932 953 return
933 954 # Do Not loop on the widget count as it change while closing
934 955 title = self.window().windowTitle()
935 956 cancel = QtGui.QMessageBox.Cancel
936 957 okay = QtGui.QMessageBox.Ok
937 958
938 959 if self.confirm_exit:
939 960 if self.tab_widget.count() > 1:
940 961 msg = "Close all tabs, stop all kernels, and Quit?"
941 962 else:
942 963 msg = "Close console, stop kernel, and Quit?"
943 964 info = "Kernels not started here (e.g. notebooks) will be left alone."
944 965 closeall = QtGui.QPushButton("&Quit", self)
945 966 closeall.setShortcut('Q')
946 967 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
947 968 title, msg)
948 969 box.setInformativeText(info)
949 970 box.addButton(cancel)
950 971 box.addButton(closeall, QtGui.QMessageBox.YesRole)
951 972 box.setDefaultButton(closeall)
952 973 box.setEscapeButton(cancel)
953 974 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
954 975 box.setIconPixmap(pixmap)
955 976 reply = box.exec_()
956 977 else:
957 978 reply = okay
958 979
959 980 if reply == cancel:
960 981 event.ignore()
961 982 return
962 983 if reply == okay:
963 984 while self.tab_widget.count() >= 1:
964 985 # prevent further confirmations:
965 986 widget = self.active_frontend
966 987 widget._confirm_exit = False
967 988 self.close_tab(widget)
968 989 event.accept()
969 990
@@ -1,243 +1,260 b''
1 1 """Various display related classes.
2 2
3 Authors : MinRK, gregcaporaso
3 Authors : MinRK, gregcaporaso, dannystaple
4 4 """
5 import urllib
5 6
6 7 from os.path import exists, isfile, splitext, abspath, join, isdir, walk
7 8
8 9
9 10 class YouTubeVideo(object):
10 11 """Class for embedding a YouTube Video in an IPython session, based on its video id.
11 12
12 13 e.g. to embed the video on this page:
13 14
14 15 http://www.youtube.com/watch?v=foo
15 16
16 17 you would do:
17 18
18 19 vid = YouTubeVideo("foo")
19 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 35 self.id = id
24 36 self.width = width
25 37 self.height = height
38 self.params = kwargs
26 39
27 40 def _repr_html_(self):
28 41 """return YouTube embed iframe for this video id"""
42 if self.params:
43 params = "?" + urllib.urlencode(self.params)
44 else:
45 params = ""
29 46 return """
30 47 <iframe
31 48 width="%i"
32 49 height="%i"
33 src="http://www.youtube.com/embed/%s"
50 src="http://www.youtube.com/embed/%s%s"
34 51 frameborder="0"
35 52 allowfullscreen
36 53 ></iframe>
37 """%(self.width, self.height, self.id)
54 """ % (self.width, self.height, self.id, params)
38 55
39 56 class FileLink(object):
40 57 """Class for embedding a local file link in an IPython session, based on path
41 58
42 59 e.g. to embed a link that was generated in the IPython notebook as my/data.txt
43 60
44 61 you would do:
45 62
46 63 local_file = FileLink("my/data.txt")
47 64 display(local_file)
48 65
49 66 or in the HTML notebook, just
50 67
51 68 FileLink("my/data.txt")
52 69 """
53 70
54 71 html_link_str = "<a href='%s' target='_blank'>%s</a>"
55 72
56 73 def __init__(self,
57 74 path,
58 75 url_prefix='files/',
59 76 result_html_prefix='',
60 77 result_html_suffix='<br>'):
61 78 """
62 79 path : path to the file or directory that should be formatted
63 80 directory_prefix : prefix to be prepended to all files to form a
64 81 working link [default: 'files']
65 82 result_html_prefix : text to append to beginning to link
66 83 [default: none]
67 84 result_html_suffix : text to append at the end of link
68 85 [default: '<br>']
69 86 """
70 87 self.path = path
71 88 self.url_prefix = url_prefix
72 89 self.result_html_prefix = result_html_prefix
73 90 self.result_html_suffix = result_html_suffix
74 91
75 92 def _format_path(self):
76 93 fp = ''.join([self.url_prefix,self.path])
77 94 return ''.join([self.result_html_prefix,
78 95 self.html_link_str % (fp, self.path),
79 96 self.result_html_suffix])
80 97
81 98 def _repr_html_(self):
82 99 """return html link to file
83 100 """
84 101 if not exists(self.path):
85 102 return ("Path (<tt>%s</tt>) doesn't exist. "
86 103 "It may still be in the process of "
87 104 "being generated, or you may have the "
88 105 "incorrect path." % self.path)
89 106
90 107 return self._format_path()
91 108
92 109 def __repr__(self):
93 110 """return absolute path to file
94 111 """
95 112 return abspath(self.path)
96 113
97 114 # Create an alias for formatting a single directory name as a link.
98 115 # Right now this is the same as a formatting for a single file, but
99 116 # we'll encourage users to reference these with a different class in
100 117 # case we want to change this in the future.
101 118 DirectoryLink = FileLink
102 119
103 120 class FileLinks(FileLink):
104 121 """Class for embedding local file links in an IPython session, based on path
105 122
106 123 e.g. to embed links to files that were generated in the IPython notebook under my/data
107 124
108 125 you would do:
109 126
110 127 local_files = FileLinks("my/data")
111 128 display(local_files)
112 129
113 130 or in the HTML notebook, just
114 131
115 132 FileLinks("my/data")
116 133
117 134 """
118 135 def __init__(self,
119 136 path,
120 137 url_prefix='files/',
121 138 included_suffixes=None,
122 139 result_html_prefix='',
123 140 result_html_suffix='<br>',
124 141 notebook_display_formatter=None,
125 142 terminal_display_formatter=None):
126 143 """
127 144 included_suffixes : list of filename suffixes to include when
128 145 formatting output [default: include all files]
129 146
130 147 See the FileLink (baseclass of LocalDirectory) docstring for
131 148 information on additional parameters.
132 149
133 150 notebook_display_formatter : func passed to os.path.walk when
134 151 formatting links for display in the notebook
135 152
136 153 terminal_display_formatter : func passed to os.path.walk when
137 154 formatting links for display in the terminal
138 155
139 156 """
140 157 self.included_suffixes = included_suffixes
141 158 # remove trailing slashs for more consistent output formatting
142 159 path = path.rstrip('/')
143 160 FileLink.__init__(self,
144 161 path,
145 162 url_prefix,
146 163 result_html_prefix,
147 164 result_html_suffix)
148 165
149 166 self.notebook_display_formatter = \
150 167 notebook_display_formatter or self._get_notebook_display_formatter()
151 168 self.terminal_display_formatter = \
152 169 terminal_display_formatter or self._get_terminal_display_formatter()
153 170
154 171 def _get_display_formatter(self,
155 172 dirname_output_format,
156 173 fname_output_format,
157 174 fp_format):
158 175 """ generate func to pass to os.path.walk
159 176
160 177 dirname_output_format: string to use for formatting directory
161 178 names, dirname will be substituted for a single "%s" which
162 179 must appear in this string
163 180 fname_output_format: string to use for formatting file names,
164 181 if a single "%s" appears in the string, fname will be substituted
165 182 if two "%s" appear in the string, the path to fname will be
166 183 substituted for the first and fname will be substituted for the
167 184 second
168 185 fp_format: string to use for formatting filepaths, must contain
169 186 exactly two "%s" and the dirname will be subsituted for the first
170 187 and fname will be substituted for the second
171 188 """
172 189
173 190 included_suffixes = self.included_suffixes
174 191
175 192 def f(output_lines, dirname, fnames):
176 193 """ func to be passed to os.path.walk """
177 194 # begin by figuring out which filenames, if any,
178 195 # are going to be displayed
179 196 display_fnames = []
180 197 for fname in fnames:
181 198 if (isfile(join(dirname,fname)) and
182 199 (included_suffixes == None or
183 200 splitext(fname)[1] in included_suffixes)):
184 201 display_fnames.append(fname)
185 202
186 203 if len(display_fnames) == 0:
187 204 # if there are no filenames to display, don't print anything
188 205 # (not even the directory name)
189 206 pass
190 207 else:
191 208 # otherwise print the formatted directory name followed by
192 209 # the formatted filenames
193 210 dirname_output_line = dirname_output_format % dirname
194 211 output_lines.append(dirname_output_line)
195 212 for fname in display_fnames:
196 213 fp = fp_format % (dirname,fname)
197 214 try:
198 215 # output can include both a filepath and a filename...
199 216 fname_output_line = fname_output_format % (fp, fname)
200 217 except TypeError:
201 218 # ... or just a single filepath
202 219 fname_output_line = fname_output_format % fname
203 220 output_lines.append(fname_output_line)
204 221 return
205 222 return f
206 223
207 224 def _get_notebook_display_formatter(self,
208 225 spacer="&nbsp;&nbsp;"):
209 226 """ generate func to pass to os.path.walk for notebook formatting
210 227 """
211 228 dirname_output_format = \
212 229 self.result_html_prefix + "%s/" + self.result_html_suffix
213 230 fname_output_format = \
214 231 self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
215 232 fp_format = self.url_prefix + '%s/%s'
216 233
217 234 return self._get_display_formatter(dirname_output_format,
218 235 fname_output_format,
219 236 fp_format)
220 237
221 238 def _get_terminal_display_formatter(self,
222 239 spacer=" "):
223 240 """ generate func to pass to os.path.walk for terminal formatting
224 241 """
225 242 dirname_output_format = "%s/"
226 243 fname_output_format = spacer + "%s"
227 244 fp_format = '%s/%s'
228 245
229 246 return self._get_display_formatter(dirname_output_format,
230 247 fname_output_format,
231 248 fp_format)
232 249
233 250 def _format_path(self):
234 251 result_lines = []
235 252 walk(self.path, self.notebook_display_formatter, result_lines)
236 253 return '\n'.join(result_lines)
237 254
238 255 def __repr__(self):
239 256 """return newline-separated absolute paths
240 257 """
241 258 result_lines = []
242 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 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Qt4's inputhook support function
4 4
5 5 Author: Christian Boos
6 6 """
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2011 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 from IPython.core.interactiveshell import InteractiveShell
20 20 from IPython.external.qt_for_kernel import QtCore, QtGui
21 21 from IPython.lib.inputhook import allow_CTRL_C, ignore_CTRL_C, stdin_ready
22 22
23 23 #-----------------------------------------------------------------------------
24 24 # Code
25 25 #-----------------------------------------------------------------------------
26 26
27 27 def create_inputhook_qt4(mgr, app=None):
28 28 """Create an input hook for running the Qt4 application event loop.
29 29
30 30 Parameters
31 31 ----------
32 32 mgr : an InputHookManager
33 33
34 34 app : Qt Application, optional.
35 35 Running application to use. If not given, we probe Qt for an
36 36 existing application object, and create a new one if none is found.
37 37
38 38 Returns
39 39 -------
40 40 A pair consisting of a Qt Application (either the one given or the
41 41 one found or created) and a inputhook.
42 42
43 43 Notes
44 44 -----
45 45 We use a custom input hook instead of PyQt4's default one, as it
46 46 interacts better with the readline packages (issue #481).
47 47
48 48 The inputhook function works in tandem with a 'pre_prompt_hook'
49 49 which automatically restores the hook as an inputhook in case the
50 50 latter has been temporarily disabled after having intercepted a
51 51 KeyboardInterrupt.
52 52 """
53 53
54 54 if app is None:
55 55 app = QtCore.QCoreApplication.instance()
56 56 if app is None:
57 57 app = QtGui.QApplication([" "])
58 58
59 59 # Re-use previously created inputhook if any
60 60 ip = InteractiveShell.instance()
61 61 if hasattr(ip, '_inputhook_qt4'):
62 62 return app, ip._inputhook_qt4
63 63
64 64 # Otherwise create the inputhook_qt4/preprompthook_qt4 pair of
65 65 # hooks (they both share the got_kbdint flag)
66 66
67 67 got_kbdint = [False]
68 68
69 69 def inputhook_qt4():
70 70 """PyOS_InputHook python hook for Qt4.
71 71
72 72 Process pending Qt events and if there's no pending keyboard
73 73 input, spend a short slice of time (50ms) running the Qt event
74 74 loop.
75 75
76 76 As a Python ctypes callback can't raise an exception, we catch
77 77 the KeyboardInterrupt and temporarily deactivate the hook,
78 78 which will let a *second* CTRL+C be processed normally and go
79 79 back to a clean prompt line.
80 80 """
81 81 try:
82 82 allow_CTRL_C()
83 83 app = QtCore.QCoreApplication.instance()
84 84 if not app: # shouldn't happen, but safer if it happens anyway...
85 85 return 0
86 86 app.processEvents(QtCore.QEventLoop.AllEvents, 300)
87 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 109 timer = QtCore.QTimer()
89 timer.timeout.connect(app.quit)
110 event_loop = QtCore.QEventLoop()
111 timer.timeout.connect(event_loop.quit)
90 112 while not stdin_ready():
91 113 timer.start(50)
92 app.exec_()
114 event_loop.exec_()
93 115 timer.stop()
94 116 except KeyboardInterrupt:
95 117 ignore_CTRL_C()
96 118 got_kbdint[0] = True
97 119 print("\nKeyboardInterrupt - Ctrl-C again for new prompt")
98 120 mgr.clear_inputhook()
99 121 except: # NO exceptions are allowed to escape from a ctypes callback
100 122 ignore_CTRL_C()
101 123 from traceback import print_exc
102 124 print_exc()
103 125 print("Got exception from inputhook_qt4, unregistering.")
104 126 mgr.clear_inputhook()
105 127 finally:
106 128 allow_CTRL_C()
107 129 return 0
108 130
109 131 def preprompthook_qt4(ishell):
110 132 """'pre_prompt_hook' used to restore the Qt4 input hook
111 133
112 134 (in case the latter was temporarily deactivated after a
113 135 CTRL+C)
114 136 """
115 137 if got_kbdint[0]:
116 138 mgr.set_inputhook(inputhook_qt4)
117 139 got_kbdint[0] = False
118 140
119 141 ip._inputhook_qt4 = inputhook_qt4
120 142 ip.set_hook('pre_prompt_hook', preprompthook_qt4)
121 143
122 144 return app, inputhook_qt4
@@ -1,398 +1,399 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 The IPython engine application
5 5
6 6 Authors:
7 7
8 8 * Brian Granger
9 9 * MinRK
10 10
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Copyright (C) 2008-2011 The IPython Development Team
15 15 #
16 16 # Distributed under the terms of the BSD License. The full license is in
17 17 # the file COPYING, distributed as part of this software.
18 18 #-----------------------------------------------------------------------------
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Imports
22 22 #-----------------------------------------------------------------------------
23 23
24 24 import json
25 25 import os
26 26 import sys
27 27 import time
28 28
29 29 import zmq
30 30 from zmq.eventloop import ioloop
31 31
32 32 from IPython.core.profiledir import ProfileDir
33 33 from IPython.parallel.apps.baseapp import (
34 34 BaseParallelApplication,
35 35 base_aliases,
36 36 base_flags,
37 37 catch_config_error,
38 38 )
39 39 from IPython.zmq.log import EnginePUBHandler
40 40 from IPython.zmq.ipkernel import Kernel, IPKernelApp
41 41 from IPython.zmq.session import (
42 42 Session, session_aliases, session_flags
43 43 )
44 from IPython.zmq.zmqshell import ZMQInteractiveShell
44 45
45 46 from IPython.config.configurable import Configurable
46 47
47 48 from IPython.parallel.engine.engine import EngineFactory
48 49 from IPython.parallel.util import disambiguate_ip_address
49 50
50 51 from IPython.utils.importstring import import_item
51 52 from IPython.utils.py3compat import cast_bytes
52 53 from IPython.utils.traitlets import Bool, Unicode, Dict, List, Float, Instance
53 54
54 55
55 56 #-----------------------------------------------------------------------------
56 57 # Module level variables
57 58 #-----------------------------------------------------------------------------
58 59
59 60 #: The default config file name for this application
60 61 default_config_file_name = u'ipengine_config.py'
61 62
62 63 _description = """Start an IPython engine for parallel computing.
63 64
64 65 IPython engines run in parallel and perform computations on behalf of a client
65 66 and controller. A controller needs to be started before the engines. The
66 67 engine can be configured using command line options or using a cluster
67 68 directory. Cluster directories contain config, log and security files and are
68 69 usually located in your ipython directory and named as "profile_name".
69 70 See the `profile` and `profile-dir` options for details.
70 71 """
71 72
72 73 _examples = """
73 74 ipengine --ip=192.168.0.1 --port=1000 # connect to hub at ip and port
74 75 ipengine --log-to-file --log-level=DEBUG # log to a file with DEBUG verbosity
75 76 """
76 77
77 78 #-----------------------------------------------------------------------------
78 79 # MPI configuration
79 80 #-----------------------------------------------------------------------------
80 81
81 82 mpi4py_init = """from mpi4py import MPI as mpi
82 83 mpi.size = mpi.COMM_WORLD.Get_size()
83 84 mpi.rank = mpi.COMM_WORLD.Get_rank()
84 85 """
85 86
86 87
87 88 pytrilinos_init = """from PyTrilinos import Epetra
88 89 class SimpleStruct:
89 90 pass
90 91 mpi = SimpleStruct()
91 92 mpi.rank = 0
92 93 mpi.size = 0
93 94 """
94 95
95 96 class MPI(Configurable):
96 97 """Configurable for MPI initialization"""
97 98 use = Unicode('', config=True,
98 99 help='How to enable MPI (mpi4py, pytrilinos, or empty string to disable).'
99 100 )
100 101
101 102 def _use_changed(self, name, old, new):
102 103 # load default init script if it's not set
103 104 if not self.init_script:
104 105 self.init_script = self.default_inits.get(new, '')
105 106
106 107 init_script = Unicode('', config=True,
107 108 help="Initialization code for MPI")
108 109
109 110 default_inits = Dict({'mpi4py' : mpi4py_init, 'pytrilinos':pytrilinos_init},
110 111 config=True)
111 112
112 113
113 114 #-----------------------------------------------------------------------------
114 115 # Main application
115 116 #-----------------------------------------------------------------------------
116 117 aliases = dict(
117 118 file = 'IPEngineApp.url_file',
118 119 c = 'IPEngineApp.startup_command',
119 120 s = 'IPEngineApp.startup_script',
120 121
121 122 url = 'EngineFactory.url',
122 123 ssh = 'EngineFactory.sshserver',
123 124 sshkey = 'EngineFactory.sshkey',
124 125 ip = 'EngineFactory.ip',
125 126 transport = 'EngineFactory.transport',
126 127 port = 'EngineFactory.regport',
127 128 location = 'EngineFactory.location',
128 129
129 130 timeout = 'EngineFactory.timeout',
130 131
131 132 mpi = 'MPI.use',
132 133
133 134 )
134 135 aliases.update(base_aliases)
135 136 aliases.update(session_aliases)
136 137 flags = {}
137 138 flags.update(base_flags)
138 139 flags.update(session_flags)
139 140
140 141 class IPEngineApp(BaseParallelApplication):
141 142
142 143 name = 'ipengine'
143 144 description = _description
144 145 examples = _examples
145 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 149 startup_script = Unicode(u'', config=True,
149 150 help='specify a script to be run at startup')
150 151 startup_command = Unicode('', config=True,
151 152 help='specify a command to be run at startup')
152 153
153 154 url_file = Unicode(u'', config=True,
154 155 help="""The full location of the file containing the connection information for
155 156 the controller. If this is not given, the file must be in the
156 157 security directory of the cluster directory. This location is
157 158 resolved using the `profile` or `profile_dir` options.""",
158 159 )
159 160 wait_for_url_file = Float(5, config=True,
160 161 help="""The maximum number of seconds to wait for url_file to exist.
161 162 This is useful for batch-systems and shared-filesystems where the
162 163 controller and engine are started at the same time and it
163 164 may take a moment for the controller to write the connector files.""")
164 165
165 166 url_file_name = Unicode(u'ipcontroller-engine.json', config=True)
166 167
167 168 def _cluster_id_changed(self, name, old, new):
168 169 if new:
169 170 base = 'ipcontroller-%s' % new
170 171 else:
171 172 base = 'ipcontroller'
172 173 self.url_file_name = "%s-engine.json" % base
173 174
174 175 log_url = Unicode('', config=True,
175 176 help="""The URL for the iploggerapp instance, for forwarding
176 177 logging to a central location.""")
177 178
178 179 # an IPKernelApp instance, used to setup listening for shell frontends
179 180 kernel_app = Instance(IPKernelApp)
180 181
181 182 aliases = Dict(aliases)
182 183 flags = Dict(flags)
183 184
184 185 @property
185 186 def kernel(self):
186 187 """allow access to the Kernel object, so I look like IPKernelApp"""
187 188 return self.engine.kernel
188 189
189 190 def find_url_file(self):
190 191 """Set the url file.
191 192
192 193 Here we don't try to actually see if it exists for is valid as that
193 194 is hadled by the connection logic.
194 195 """
195 196 config = self.config
196 197 # Find the actual controller key file
197 198 if not self.url_file:
198 199 self.url_file = os.path.join(
199 200 self.profile_dir.security_dir,
200 201 self.url_file_name
201 202 )
202 203
203 204 def load_connector_file(self):
204 205 """load config from a JSON connector file,
205 206 at a *lower* priority than command-line/config files.
206 207 """
207 208
208 209 self.log.info("Loading url_file %r", self.url_file)
209 210 config = self.config
210 211
211 212 with open(self.url_file) as f:
212 213 d = json.loads(f.read())
213 214
214 215 # allow hand-override of location for disambiguation
215 216 # and ssh-server
216 217 try:
217 218 config.EngineFactory.location
218 219 except AttributeError:
219 220 config.EngineFactory.location = d['location']
220 221
221 222 try:
222 223 config.EngineFactory.sshserver
223 224 except AttributeError:
224 225 config.EngineFactory.sshserver = d.get('ssh')
225 226
226 227 location = config.EngineFactory.location
227 228
228 229 proto, ip = d['interface'].split('://')
229 230 ip = disambiguate_ip_address(ip, location)
230 231 d['interface'] = '%s://%s' % (proto, ip)
231 232
232 233 # DO NOT allow override of basic URLs, serialization, or exec_key
233 234 # JSON file takes top priority there
234 235 config.Session.key = cast_bytes(d['exec_key'])
235 236
236 237 config.EngineFactory.url = d['interface'] + ':%i' % d['registration']
237 238
238 239 config.Session.packer = d['pack']
239 240 config.Session.unpacker = d['unpack']
240 241
241 242 self.log.debug("Config changed:")
242 243 self.log.debug("%r", config)
243 244 self.connection_info = d
244 245
245 246 def bind_kernel(self, **kwargs):
246 247 """Promote engine to listening kernel, accessible to frontends."""
247 248 if self.kernel_app is not None:
248 249 return
249 250
250 251 self.log.info("Opening ports for direct connections as an IPython kernel")
251 252
252 253 kernel = self.kernel
253 254
254 255 kwargs.setdefault('config', self.config)
255 256 kwargs.setdefault('log', self.log)
256 257 kwargs.setdefault('profile_dir', self.profile_dir)
257 258 kwargs.setdefault('session', self.engine.session)
258 259
259 260 app = self.kernel_app = IPKernelApp(**kwargs)
260 261
261 262 # allow IPKernelApp.instance():
262 263 IPKernelApp._instance = app
263 264
264 265 app.init_connection_file()
265 266 # relevant contents of init_sockets:
266 267
267 268 app.shell_port = app._bind_socket(kernel.shell_streams[0], app.shell_port)
268 269 app.log.debug("shell ROUTER Channel on port: %i", app.shell_port)
269 270
270 271 app.iopub_port = app._bind_socket(kernel.iopub_socket, app.iopub_port)
271 272 app.log.debug("iopub PUB Channel on port: %i", app.iopub_port)
272 273
273 274 kernel.stdin_socket = self.engine.context.socket(zmq.ROUTER)
274 275 app.stdin_port = app._bind_socket(kernel.stdin_socket, app.stdin_port)
275 276 app.log.debug("stdin ROUTER Channel on port: %i", app.stdin_port)
276 277
277 278 # start the heartbeat, and log connection info:
278 279
279 280 app.init_heartbeat()
280 281
281 282 app.log_connection_info()
282 283 app.write_connection_file()
283 284
284 285
285 286 def init_engine(self):
286 287 # This is the working dir by now.
287 288 sys.path.insert(0, '')
288 289 config = self.config
289 290 # print config
290 291 self.find_url_file()
291 292
292 293 # was the url manually specified?
293 294 keys = set(self.config.EngineFactory.keys())
294 295 keys = keys.union(set(self.config.RegistrationFactory.keys()))
295 296
296 297 if keys.intersection(set(['ip', 'url', 'port'])):
297 298 # Connection info was specified, don't wait for the file
298 299 url_specified = True
299 300 self.wait_for_url_file = 0
300 301 else:
301 302 url_specified = False
302 303
303 304 if self.wait_for_url_file and not os.path.exists(self.url_file):
304 305 self.log.warn("url_file %r not found", self.url_file)
305 306 self.log.warn("Waiting up to %.1f seconds for it to arrive.", self.wait_for_url_file)
306 307 tic = time.time()
307 308 while not os.path.exists(self.url_file) and (time.time()-tic < self.wait_for_url_file):
308 309 # wait for url_file to exist, or until time limit
309 310 time.sleep(0.1)
310 311
311 312 if os.path.exists(self.url_file):
312 313 self.load_connector_file()
313 314 elif not url_specified:
314 315 self.log.fatal("Fatal: url file never arrived: %s", self.url_file)
315 316 self.exit(1)
316 317
317 318
318 319 try:
319 320 exec_lines = config.IPKernelApp.exec_lines
320 321 except AttributeError:
321 322 try:
322 323 exec_lines = config.InteractiveShellApp.exec_lines
323 324 except AttributeError:
324 325 exec_lines = config.IPKernelApp.exec_lines = []
325 326 try:
326 327 exec_files = config.IPKernelApp.exec_files
327 328 except AttributeError:
328 329 try:
329 330 exec_files = config.InteractiveShellApp.exec_files
330 331 except AttributeError:
331 332 exec_files = config.IPKernelApp.exec_files = []
332 333
333 334 if self.startup_script:
334 335 exec_files.append(self.startup_script)
335 336 if self.startup_command:
336 337 exec_lines.append(self.startup_command)
337 338
338 339 # Create the underlying shell class and Engine
339 340 # shell_class = import_item(self.master_config.Global.shell_class)
340 341 # print self.config
341 342 try:
342 343 self.engine = EngineFactory(config=config, log=self.log,
343 344 connection_info=self.connection_info,
344 345 )
345 346 except:
346 347 self.log.error("Couldn't start the Engine", exc_info=True)
347 348 self.exit(1)
348 349
349 350 def forward_logging(self):
350 351 if self.log_url:
351 352 self.log.info("Forwarding logging to %s", self.log_url)
352 353 context = self.engine.context
353 354 lsock = context.socket(zmq.PUB)
354 355 lsock.connect(self.log_url)
355 356 handler = EnginePUBHandler(self.engine, lsock)
356 357 handler.setLevel(self.log_level)
357 358 self.log.addHandler(handler)
358 359
359 360 def init_mpi(self):
360 361 global mpi
361 362 self.mpi = MPI(config=self.config)
362 363
363 364 mpi_import_statement = self.mpi.init_script
364 365 if mpi_import_statement:
365 366 try:
366 367 self.log.info("Initializing MPI:")
367 368 self.log.info(mpi_import_statement)
368 369 exec mpi_import_statement in globals()
369 370 except:
370 371 mpi = None
371 372 else:
372 373 mpi = None
373 374
374 375 @catch_config_error
375 376 def initialize(self, argv=None):
376 377 super(IPEngineApp, self).initialize(argv)
377 378 self.init_mpi()
378 379 self.init_engine()
379 380 self.forward_logging()
380 381
381 382 def start(self):
382 383 self.engine.start()
383 384 try:
384 385 self.engine.loop.start()
385 386 except KeyboardInterrupt:
386 387 self.log.critical("Engine Interrupted, shutting down...\n")
387 388
388 389
389 390 def launch_new_instance():
390 391 """Create and run the IPython engine"""
391 392 app = IPEngineApp.instance()
392 393 app.initialize()
393 394 app.start()
394 395
395 396
396 397 if __name__ == '__main__':
397 398 launch_new_instance()
398 399
@@ -1,1345 +1,1354 b''
1 1 # encoding: utf-8
2 2 """
3 3 Facilities for launching IPython processes asynchronously.
4 4
5 5 Authors:
6 6
7 7 * Brian Granger
8 8 * MinRK
9 9 """
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Copyright (C) 2008-2011 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is in
15 15 # the file COPYING, distributed as part of this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 22 import copy
23 23 import logging
24 24 import os
25 25 import pipes
26 26 import stat
27 27 import sys
28 28 import time
29 29
30 30 # signal imports, handling various platforms, versions
31 31
32 32 from signal import SIGINT, SIGTERM
33 33 try:
34 34 from signal import SIGKILL
35 35 except ImportError:
36 36 # Windows
37 37 SIGKILL=SIGTERM
38 38
39 39 try:
40 40 # Windows >= 2.7, 3.2
41 41 from signal import CTRL_C_EVENT as SIGINT
42 42 except ImportError:
43 43 pass
44 44
45 45 from subprocess import Popen, PIPE, STDOUT
46 46 try:
47 47 from subprocess import check_output
48 48 except ImportError:
49 49 # pre-2.7, define check_output with Popen
50 50 def check_output(*args, **kwargs):
51 51 kwargs.update(dict(stdout=PIPE))
52 52 p = Popen(*args, **kwargs)
53 53 out,err = p.communicate()
54 54 return out
55 55
56 56 from zmq.eventloop import ioloop
57 57
58 58 from IPython.config.application import Application
59 59 from IPython.config.configurable import LoggingConfigurable
60 60 from IPython.utils.text import EvalFormatter
61 61 from IPython.utils.traitlets import (
62 62 Any, Integer, CFloat, List, Unicode, Dict, Instance, HasTraits, CRegExp
63 63 )
64 64 from IPython.utils.path import get_home_dir
65 65 from IPython.utils.process import find_cmd, FindCmdError
66 66
67 67 from .win32support import forward_read_events
68 68
69 69 from .winhpcjob import IPControllerTask, IPEngineTask, IPControllerJob, IPEngineSetJob
70 70
71 71 WINDOWS = os.name == 'nt'
72 72
73 73 #-----------------------------------------------------------------------------
74 74 # Paths to the kernel apps
75 75 #-----------------------------------------------------------------------------
76 76
77 77 cmd = "from IPython.parallel.apps.%s import launch_new_instance; launch_new_instance()"
78 78
79 79 ipcluster_cmd_argv = [sys.executable, "-c", cmd % "ipclusterapp"]
80 80
81 81 ipengine_cmd_argv = [sys.executable, "-c", cmd % "ipengineapp"]
82 82
83 83 ipcontroller_cmd_argv = [sys.executable, "-c", cmd % "ipcontrollerapp"]
84 84
85 85 #-----------------------------------------------------------------------------
86 86 # Base launchers and errors
87 87 #-----------------------------------------------------------------------------
88 88
89 89
90 90 class LauncherError(Exception):
91 91 pass
92 92
93 93
94 94 class ProcessStateError(LauncherError):
95 95 pass
96 96
97 97
98 98 class UnknownStatus(LauncherError):
99 99 pass
100 100
101 101
102 102 class BaseLauncher(LoggingConfigurable):
103 103 """An asbtraction for starting, stopping and signaling a process."""
104 104
105 105 # In all of the launchers, the work_dir is where child processes will be
106 106 # run. This will usually be the profile_dir, but may not be. any work_dir
107 107 # passed into the __init__ method will override the config value.
108 108 # This should not be used to set the work_dir for the actual engine
109 109 # and controller. Instead, use their own config files or the
110 110 # controller_args, engine_args attributes of the launchers to add
111 111 # the work_dir option.
112 112 work_dir = Unicode(u'.')
113 113 loop = Instance('zmq.eventloop.ioloop.IOLoop')
114 114
115 115 start_data = Any()
116 116 stop_data = Any()
117 117
118 118 def _loop_default(self):
119 119 return ioloop.IOLoop.instance()
120 120
121 121 def __init__(self, work_dir=u'.', config=None, **kwargs):
122 122 super(BaseLauncher, self).__init__(work_dir=work_dir, config=config, **kwargs)
123 123 self.state = 'before' # can be before, running, after
124 124 self.stop_callbacks = []
125 125 self.start_data = None
126 126 self.stop_data = None
127 127
128 128 @property
129 129 def args(self):
130 130 """A list of cmd and args that will be used to start the process.
131 131
132 132 This is what is passed to :func:`spawnProcess` and the first element
133 133 will be the process name.
134 134 """
135 135 return self.find_args()
136 136
137 137 def find_args(self):
138 138 """The ``.args`` property calls this to find the args list.
139 139
140 140 Subcommand should implement this to construct the cmd and args.
141 141 """
142 142 raise NotImplementedError('find_args must be implemented in a subclass')
143 143
144 144 @property
145 145 def arg_str(self):
146 146 """The string form of the program arguments."""
147 147 return ' '.join(self.args)
148 148
149 149 @property
150 150 def running(self):
151 151 """Am I running."""
152 152 if self.state == 'running':
153 153 return True
154 154 else:
155 155 return False
156 156
157 157 def start(self):
158 158 """Start the process."""
159 159 raise NotImplementedError('start must be implemented in a subclass')
160 160
161 161 def stop(self):
162 162 """Stop the process and notify observers of stopping.
163 163
164 164 This method will return None immediately.
165 165 To observe the actual process stopping, see :meth:`on_stop`.
166 166 """
167 167 raise NotImplementedError('stop must be implemented in a subclass')
168 168
169 169 def on_stop(self, f):
170 170 """Register a callback to be called with this Launcher's stop_data
171 171 when the process actually finishes.
172 172 """
173 173 if self.state=='after':
174 174 return f(self.stop_data)
175 175 else:
176 176 self.stop_callbacks.append(f)
177 177
178 178 def notify_start(self, data):
179 179 """Call this to trigger startup actions.
180 180
181 181 This logs the process startup and sets the state to 'running'. It is
182 182 a pass-through so it can be used as a callback.
183 183 """
184 184
185 185 self.log.debug('Process %r started: %r', self.args[0], data)
186 186 self.start_data = data
187 187 self.state = 'running'
188 188 return data
189 189
190 190 def notify_stop(self, data):
191 191 """Call this to trigger process stop actions.
192 192
193 193 This logs the process stopping and sets the state to 'after'. Call
194 194 this to trigger callbacks registered via :meth:`on_stop`."""
195 195
196 196 self.log.debug('Process %r stopped: %r', self.args[0], data)
197 197 self.stop_data = data
198 198 self.state = 'after'
199 199 for i in range(len(self.stop_callbacks)):
200 200 d = self.stop_callbacks.pop()
201 201 d(data)
202 202 return data
203 203
204 204 def signal(self, sig):
205 205 """Signal the process.
206 206
207 207 Parameters
208 208 ----------
209 209 sig : str or int
210 210 'KILL', 'INT', etc., or any signal number
211 211 """
212 212 raise NotImplementedError('signal must be implemented in a subclass')
213 213
214 214 class ClusterAppMixin(HasTraits):
215 215 """MixIn for cluster args as traits"""
216 216 profile_dir=Unicode('')
217 217 cluster_id=Unicode('')
218 218
219 219 @property
220 220 def cluster_args(self):
221 221 return ['--profile-dir', self.profile_dir, '--cluster-id', self.cluster_id]
222 222
223 223 class ControllerMixin(ClusterAppMixin):
224 224 controller_cmd = List(ipcontroller_cmd_argv, config=True,
225 225 help="""Popen command to launch ipcontroller.""")
226 226 # Command line arguments to ipcontroller.
227 227 controller_args = List(['--log-to-file','--log-level=%i' % logging.INFO], config=True,
228 228 help="""command-line args to pass to ipcontroller""")
229 229
230 230 class EngineMixin(ClusterAppMixin):
231 231 engine_cmd = List(ipengine_cmd_argv, config=True,
232 232 help="""command to launch the Engine.""")
233 233 # Command line arguments for ipengine.
234 234 engine_args = List(['--log-to-file','--log-level=%i' % logging.INFO], config=True,
235 235 help="command-line arguments to pass to ipengine"
236 236 )
237 237
238 238
239 239 #-----------------------------------------------------------------------------
240 240 # Local process launchers
241 241 #-----------------------------------------------------------------------------
242 242
243 243
244 244 class LocalProcessLauncher(BaseLauncher):
245 245 """Start and stop an external process in an asynchronous manner.
246 246
247 247 This will launch the external process with a working directory of
248 248 ``self.work_dir``.
249 249 """
250 250
251 251 # This is used to to construct self.args, which is passed to
252 252 # spawnProcess.
253 253 cmd_and_args = List([])
254 254 poll_frequency = Integer(100) # in ms
255 255
256 256 def __init__(self, work_dir=u'.', config=None, **kwargs):
257 257 super(LocalProcessLauncher, self).__init__(
258 258 work_dir=work_dir, config=config, **kwargs
259 259 )
260 260 self.process = None
261 261 self.poller = None
262 262
263 263 def find_args(self):
264 264 return self.cmd_and_args
265 265
266 266 def start(self):
267 267 self.log.debug("Starting %s: %r", self.__class__.__name__, self.args)
268 268 if self.state == 'before':
269 269 self.process = Popen(self.args,
270 270 stdout=PIPE,stderr=PIPE,stdin=PIPE,
271 271 env=os.environ,
272 272 cwd=self.work_dir
273 273 )
274 274 if WINDOWS:
275 275 self.stdout = forward_read_events(self.process.stdout)
276 276 self.stderr = forward_read_events(self.process.stderr)
277 277 else:
278 278 self.stdout = self.process.stdout.fileno()
279 279 self.stderr = self.process.stderr.fileno()
280 280 self.loop.add_handler(self.stdout, self.handle_stdout, self.loop.READ)
281 281 self.loop.add_handler(self.stderr, self.handle_stderr, self.loop.READ)
282 282 self.poller = ioloop.PeriodicCallback(self.poll, self.poll_frequency, self.loop)
283 283 self.poller.start()
284 284 self.notify_start(self.process.pid)
285 285 else:
286 286 s = 'The process was already started and has state: %r' % self.state
287 287 raise ProcessStateError(s)
288 288
289 289 def stop(self):
290 290 return self.interrupt_then_kill()
291 291
292 292 def signal(self, sig):
293 293 if self.state == 'running':
294 294 if WINDOWS and sig != SIGINT:
295 295 # use Windows tree-kill for better child cleanup
296 296 check_output(['taskkill', '-pid', str(self.process.pid), '-t', '-f'])
297 297 else:
298 298 self.process.send_signal(sig)
299 299
300 300 def interrupt_then_kill(self, delay=2.0):
301 301 """Send INT, wait a delay and then send KILL."""
302 302 try:
303 303 self.signal(SIGINT)
304 304 except Exception:
305 305 self.log.debug("interrupt failed")
306 306 pass
307 307 self.killer = ioloop.DelayedCallback(lambda : self.signal(SIGKILL), delay*1000, self.loop)
308 308 self.killer.start()
309 309
310 310 # callbacks, etc:
311 311
312 312 def handle_stdout(self, fd, events):
313 313 if WINDOWS:
314 314 line = self.stdout.recv()
315 315 else:
316 316 line = self.process.stdout.readline()
317 317 # a stopped process will be readable but return empty strings
318 318 if line:
319 319 self.log.debug(line[:-1])
320 320 else:
321 321 self.poll()
322 322
323 323 def handle_stderr(self, fd, events):
324 324 if WINDOWS:
325 325 line = self.stderr.recv()
326 326 else:
327 327 line = self.process.stderr.readline()
328 328 # a stopped process will be readable but return empty strings
329 329 if line:
330 330 self.log.debug(line[:-1])
331 331 else:
332 332 self.poll()
333 333
334 334 def poll(self):
335 335 status = self.process.poll()
336 336 if status is not None:
337 337 self.poller.stop()
338 338 self.loop.remove_handler(self.stdout)
339 339 self.loop.remove_handler(self.stderr)
340 340 self.notify_stop(dict(exit_code=status, pid=self.process.pid))
341 341 return status
342 342
343 343 class LocalControllerLauncher(LocalProcessLauncher, ControllerMixin):
344 344 """Launch a controller as a regular external process."""
345 345
346 346 def find_args(self):
347 347 return self.controller_cmd + self.cluster_args + self.controller_args
348 348
349 349 def start(self):
350 350 """Start the controller by profile_dir."""
351 351 return super(LocalControllerLauncher, self).start()
352 352
353 353
354 354 class LocalEngineLauncher(LocalProcessLauncher, EngineMixin):
355 355 """Launch a single engine as a regular externall process."""
356 356
357 357 def find_args(self):
358 358 return self.engine_cmd + self.cluster_args + self.engine_args
359 359
360 360
361 361 class LocalEngineSetLauncher(LocalEngineLauncher):
362 362 """Launch a set of engines as regular external processes."""
363 363
364 364 delay = CFloat(0.1, config=True,
365 365 help="""delay (in seconds) between starting each engine after the first.
366 366 This can help force the engines to get their ids in order, or limit
367 367 process flood when starting many engines."""
368 368 )
369 369
370 370 # launcher class
371 371 launcher_class = LocalEngineLauncher
372 372
373 373 launchers = Dict()
374 374 stop_data = Dict()
375 375
376 376 def __init__(self, work_dir=u'.', config=None, **kwargs):
377 377 super(LocalEngineSetLauncher, self).__init__(
378 378 work_dir=work_dir, config=config, **kwargs
379 379 )
380 380 self.stop_data = {}
381 381
382 382 def start(self, n):
383 383 """Start n engines by profile or profile_dir."""
384 384 dlist = []
385 385 for i in range(n):
386 386 if i > 0:
387 387 time.sleep(self.delay)
388 388 el = self.launcher_class(work_dir=self.work_dir, config=self.config, log=self.log,
389 389 profile_dir=self.profile_dir, cluster_id=self.cluster_id,
390 390 )
391 391
392 392 # Copy the engine args over to each engine launcher.
393 393 el.engine_cmd = copy.deepcopy(self.engine_cmd)
394 394 el.engine_args = copy.deepcopy(self.engine_args)
395 395 el.on_stop(self._notice_engine_stopped)
396 396 d = el.start()
397 397 self.launchers[i] = el
398 398 dlist.append(d)
399 399 self.notify_start(dlist)
400 400 return dlist
401 401
402 402 def find_args(self):
403 403 return ['engine set']
404 404
405 405 def signal(self, sig):
406 406 dlist = []
407 407 for el in self.launchers.itervalues():
408 408 d = el.signal(sig)
409 409 dlist.append(d)
410 410 return dlist
411 411
412 412 def interrupt_then_kill(self, delay=1.0):
413 413 dlist = []
414 414 for el in self.launchers.itervalues():
415 415 d = el.interrupt_then_kill(delay)
416 416 dlist.append(d)
417 417 return dlist
418 418
419 419 def stop(self):
420 420 return self.interrupt_then_kill()
421 421
422 422 def _notice_engine_stopped(self, data):
423 423 pid = data['pid']
424 424 for idx,el in self.launchers.iteritems():
425 425 if el.process.pid == pid:
426 426 break
427 427 self.launchers.pop(idx)
428 428 self.stop_data[idx] = data
429 429 if not self.launchers:
430 430 self.notify_stop(self.stop_data)
431 431
432 432
433 433 #-----------------------------------------------------------------------------
434 434 # MPI launchers
435 435 #-----------------------------------------------------------------------------
436 436
437 437
438 438 class MPILauncher(LocalProcessLauncher):
439 439 """Launch an external process using mpiexec."""
440 440
441 441 mpi_cmd = List(['mpiexec'], config=True,
442 442 help="The mpiexec command to use in starting the process."
443 443 )
444 444 mpi_args = List([], config=True,
445 445 help="The command line arguments to pass to mpiexec."
446 446 )
447 447 program = List(['date'],
448 448 help="The program to start via mpiexec.")
449 449 program_args = List([],
450 450 help="The command line argument to the program."
451 451 )
452 452 n = Integer(1)
453 453
454 454 def __init__(self, *args, **kwargs):
455 455 # deprecation for old MPIExec names:
456 456 config = kwargs.get('config', {})
457 457 for oldname in ('MPIExecLauncher', 'MPIExecControllerLauncher', 'MPIExecEngineSetLauncher'):
458 458 deprecated = config.get(oldname)
459 459 if deprecated:
460 460 newname = oldname.replace('MPIExec', 'MPI')
461 461 config[newname].update(deprecated)
462 462 self.log.warn("WARNING: %s name has been deprecated, use %s", oldname, newname)
463 463
464 464 super(MPILauncher, self).__init__(*args, **kwargs)
465 465
466 466 def find_args(self):
467 467 """Build self.args using all the fields."""
468 468 return self.mpi_cmd + ['-n', str(self.n)] + self.mpi_args + \
469 469 self.program + self.program_args
470 470
471 471 def start(self, n):
472 472 """Start n instances of the program using mpiexec."""
473 473 self.n = n
474 474 return super(MPILauncher, self).start()
475 475
476 476
477 477 class MPIControllerLauncher(MPILauncher, ControllerMixin):
478 478 """Launch a controller using mpiexec."""
479 479
480 480 # alias back to *non-configurable* program[_args] for use in find_args()
481 481 # this way all Controller/EngineSetLaunchers have the same form, rather
482 482 # than *some* having `program_args` and others `controller_args`
483 483 @property
484 484 def program(self):
485 485 return self.controller_cmd
486 486
487 487 @property
488 488 def program_args(self):
489 489 return self.cluster_args + self.controller_args
490 490
491 491 def start(self):
492 492 """Start the controller by profile_dir."""
493 493 return super(MPIControllerLauncher, self).start(1)
494 494
495 495
496 496 class MPIEngineSetLauncher(MPILauncher, EngineMixin):
497 497 """Launch engines using mpiexec"""
498 498
499 499 # alias back to *non-configurable* program[_args] for use in find_args()
500 500 # this way all Controller/EngineSetLaunchers have the same form, rather
501 501 # than *some* having `program_args` and others `controller_args`
502 502 @property
503 503 def program(self):
504 504 return self.engine_cmd
505 505
506 506 @property
507 507 def program_args(self):
508 508 return self.cluster_args + self.engine_args
509 509
510 510 def start(self, n):
511 511 """Start n engines by profile or profile_dir."""
512 512 self.n = n
513 513 return super(MPIEngineSetLauncher, self).start(n)
514 514
515 515 # deprecated MPIExec names
516 516 class DeprecatedMPILauncher(object):
517 517 def warn(self):
518 518 oldname = self.__class__.__name__
519 519 newname = oldname.replace('MPIExec', 'MPI')
520 520 self.log.warn("WARNING: %s name is deprecated, use %s", oldname, newname)
521 521
522 522 class MPIExecLauncher(MPILauncher, DeprecatedMPILauncher):
523 523 """Deprecated, use MPILauncher"""
524 524 def __init__(self, *args, **kwargs):
525 525 super(MPIExecLauncher, self).__init__(*args, **kwargs)
526 526 self.warn()
527 527
528 528 class MPIExecControllerLauncher(MPIControllerLauncher, DeprecatedMPILauncher):
529 529 """Deprecated, use MPIControllerLauncher"""
530 530 def __init__(self, *args, **kwargs):
531 531 super(MPIExecControllerLauncher, self).__init__(*args, **kwargs)
532 532 self.warn()
533 533
534 534 class MPIExecEngineSetLauncher(MPIEngineSetLauncher, DeprecatedMPILauncher):
535 535 """Deprecated, use MPIEngineSetLauncher"""
536 536 def __init__(self, *args, **kwargs):
537 537 super(MPIExecEngineSetLauncher, self).__init__(*args, **kwargs)
538 538 self.warn()
539 539
540 540
541 541 #-----------------------------------------------------------------------------
542 542 # SSH launchers
543 543 #-----------------------------------------------------------------------------
544 544
545 545 # TODO: Get SSH Launcher back to level of sshx in 0.10.2
546 546
547 547 class SSHLauncher(LocalProcessLauncher):
548 548 """A minimal launcher for ssh.
549 549
550 550 To be useful this will probably have to be extended to use the ``sshx``
551 551 idea for environment variables. There could be other things this needs
552 552 as well.
553 553 """
554 554
555 555 ssh_cmd = List(['ssh'], config=True,
556 556 help="command for starting ssh")
557 557 ssh_args = List(['-tt'], config=True,
558 558 help="args to pass to ssh")
559 559 scp_cmd = List(['scp'], config=True,
560 560 help="command for sending files")
561 561 program = List(['date'],
562 562 help="Program to launch via ssh")
563 563 program_args = List([],
564 564 help="args to pass to remote program")
565 565 hostname = Unicode('', config=True,
566 566 help="hostname on which to launch the program")
567 567 user = Unicode('', config=True,
568 568 help="username for ssh")
569 569 location = Unicode('', config=True,
570 570 help="user@hostname location for ssh in one setting")
571 571 to_fetch = List([], config=True,
572 572 help="List of (remote, local) files to fetch after starting")
573 573 to_send = List([], config=True,
574 574 help="List of (local, remote) files to send before starting")
575 575
576 576 def _hostname_changed(self, name, old, new):
577 577 if self.user:
578 578 self.location = u'%s@%s' % (self.user, new)
579 579 else:
580 580 self.location = new
581 581
582 582 def _user_changed(self, name, old, new):
583 583 self.location = u'%s@%s' % (new, self.hostname)
584 584
585 585 def find_args(self):
586 586 return self.ssh_cmd + self.ssh_args + [self.location] + \
587 587 list(map(pipes.quote, self.program + self.program_args))
588 588
589 589 def _send_file(self, local, remote):
590 590 """send a single file"""
591 591 remote = "%s:%s" % (self.location, remote)
592 592 for i in range(10):
593 593 if not os.path.exists(local):
594 594 self.log.debug("waiting for %s" % local)
595 595 time.sleep(1)
596 596 else:
597 597 break
598 598 self.log.info("sending %s to %s", local, remote)
599 599 check_output(self.scp_cmd + [local, remote])
600 600
601 601 def send_files(self):
602 602 """send our files (called before start)"""
603 603 if not self.to_send:
604 604 return
605 605 for local_file, remote_file in self.to_send:
606 606 self._send_file(local_file, remote_file)
607 607
608 608 def _fetch_file(self, remote, local):
609 609 """fetch a single file"""
610 610 full_remote = "%s:%s" % (self.location, remote)
611 611 self.log.info("fetching %s from %s", local, full_remote)
612 612 for i in range(10):
613 613 # wait up to 10s for remote file to exist
614 614 check = check_output(self.ssh_cmd + self.ssh_args + \
615 615 [self.location, 'test -e', remote, "&& echo 'yes' || echo 'no'"])
616 616 check = check.strip()
617 617 if check == 'no':
618 618 time.sleep(1)
619 619 elif check == 'yes':
620 620 break
621 621 check_output(self.scp_cmd + [full_remote, local])
622 622
623 623 def fetch_files(self):
624 624 """fetch remote files (called after start)"""
625 625 if not self.to_fetch:
626 626 return
627 627 for remote_file, local_file in self.to_fetch:
628 628 self._fetch_file(remote_file, local_file)
629 629
630 630 def start(self, hostname=None, user=None):
631 631 if hostname is not None:
632 632 self.hostname = hostname
633 633 if user is not None:
634 634 self.user = user
635 635
636 636 self.send_files()
637 637 super(SSHLauncher, self).start()
638 638 self.fetch_files()
639 639
640 640 def signal(self, sig):
641 641 if self.state == 'running':
642 642 # send escaped ssh connection-closer
643 643 self.process.stdin.write('~.')
644 644 self.process.stdin.flush()
645 645
646 646 class SSHClusterLauncher(SSHLauncher):
647 647
648 648 remote_profile_dir = Unicode('', config=True,
649 649 help="""The remote profile_dir to use.
650 650
651 651 If not specified, use calling profile, stripping out possible leading homedir.
652 652 """)
653
654 def _remote_profile_dir_default(self):
655 """turns /home/you/.ipython/profile_foo into .ipython/profile_foo
656 """
653
654 def _profile_dir_changed(self, name, old, new):
655 if not self.remote_profile_dir:
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 663 home = get_home_dir()
658 664 if not home.endswith('/'):
659 665 home = home+'/'
660 666
661 if self.profile_dir.startswith(home):
662 return self.profile_dir[len(home):]
667 if path.startswith(home):
668 return path[len(home):]
663 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 675 def _cluster_id_changed(self, name, old, new):
667 676 if new:
668 677 raise ValueError("cluster id not supported by SSH launchers")
669 678
670 679 @property
671 680 def cluster_args(self):
672 681 return ['--profile-dir', self.remote_profile_dir]
673 682
674 683 class SSHControllerLauncher(SSHClusterLauncher, ControllerMixin):
675 684
676 685 # alias back to *non-configurable* program[_args] for use in find_args()
677 686 # this way all Controller/EngineSetLaunchers have the same form, rather
678 687 # than *some* having `program_args` and others `controller_args`
679 688
680 689 def _controller_cmd_default(self):
681 690 return ['ipcontroller']
682 691
683 692 @property
684 693 def program(self):
685 694 return self.controller_cmd
686 695
687 696 @property
688 697 def program_args(self):
689 698 return self.cluster_args + self.controller_args
690 699
691 700 def _to_fetch_default(self):
692 701 return [
693 702 (os.path.join(self.remote_profile_dir, 'security', cf),
694 703 os.path.join(self.profile_dir, 'security', cf),)
695 704 for cf in ('ipcontroller-client.json', 'ipcontroller-engine.json')
696 705 ]
697 706
698 707 class SSHEngineLauncher(SSHClusterLauncher, EngineMixin):
699 708
700 709 # alias back to *non-configurable* program[_args] for use in find_args()
701 710 # this way all Controller/EngineSetLaunchers have the same form, rather
702 711 # than *some* having `program_args` and others `controller_args`
703 712
704 713 def _engine_cmd_default(self):
705 714 return ['ipengine']
706 715
707 716 @property
708 717 def program(self):
709 718 return self.engine_cmd
710 719
711 720 @property
712 721 def program_args(self):
713 722 return self.cluster_args + self.engine_args
714 723
715 724 def _to_send_default(self):
716 725 return [
717 726 (os.path.join(self.profile_dir, 'security', cf),
718 727 os.path.join(self.remote_profile_dir, 'security', cf))
719 728 for cf in ('ipcontroller-client.json', 'ipcontroller-engine.json')
720 729 ]
721 730
722 731
723 732 class SSHEngineSetLauncher(LocalEngineSetLauncher):
724 733 launcher_class = SSHEngineLauncher
725 734 engines = Dict(config=True,
726 735 help="""dict of engines to launch. This is a dict by hostname of ints,
727 736 corresponding to the number of engines to start on that host.""")
728 737
729 738 def _engine_cmd_default(self):
730 739 return ['ipengine']
731 740
732 741 @property
733 742 def engine_count(self):
734 743 """determine engine count from `engines` dict"""
735 744 count = 0
736 745 for n in self.engines.itervalues():
737 746 if isinstance(n, (tuple,list)):
738 747 n,args = n
739 748 count += n
740 749 return count
741 750
742 751 def start(self, n):
743 752 """Start engines by profile or profile_dir.
744 753 `n` is ignored, and the `engines` config property is used instead.
745 754 """
746 755
747 756 dlist = []
748 757 for host, n in self.engines.iteritems():
749 758 if isinstance(n, (tuple, list)):
750 759 n, args = n
751 760 else:
752 761 args = copy.deepcopy(self.engine_args)
753 762
754 763 if '@' in host:
755 764 user,host = host.split('@',1)
756 765 else:
757 766 user=None
758 767 for i in range(n):
759 768 if i > 0:
760 769 time.sleep(self.delay)
761 770 el = self.launcher_class(work_dir=self.work_dir, config=self.config, log=self.log,
762 771 profile_dir=self.profile_dir, cluster_id=self.cluster_id,
763 772 )
764 773 if i > 0:
765 774 # only send files for the first engine on each host
766 775 el.to_send = []
767 776
768 777 # Copy the engine args over to each engine launcher.
769 778 el.engine_cmd = self.engine_cmd
770 779 el.engine_args = args
771 780 el.on_stop(self._notice_engine_stopped)
772 781 d = el.start(user=user, hostname=host)
773 782 self.launchers[ "%s/%i" % (host,i) ] = el
774 783 dlist.append(d)
775 784 self.notify_start(dlist)
776 785 return dlist
777 786
778 787
779 788 class SSHProxyEngineSetLauncher(SSHClusterLauncher):
780 789 """Launcher for calling
781 790 `ipcluster engines` on a remote machine.
782 791
783 792 Requires that remote profile is already configured.
784 793 """
785 794
786 795 n = Integer()
787 796 ipcluster_cmd = List(['ipcluster'], config=True)
788 797
789 798 @property
790 799 def program(self):
791 800 return self.ipcluster_cmd + ['engines']
792 801
793 802 @property
794 803 def program_args(self):
795 804 return ['-n', str(self.n), '--profile-dir', self.remote_profile_dir]
796 805
797 806 def _to_send_default(self):
798 807 return [
799 808 (os.path.join(self.profile_dir, 'security', cf),
800 809 os.path.join(self.remote_profile_dir, 'security', cf))
801 810 for cf in ('ipcontroller-client.json', 'ipcontroller-engine.json')
802 811 ]
803 812
804 813 def start(self, n):
805 814 self.n = n
806 815 super(SSHProxyEngineSetLauncher, self).start()
807 816
808 817
809 818 #-----------------------------------------------------------------------------
810 819 # Windows HPC Server 2008 scheduler launchers
811 820 #-----------------------------------------------------------------------------
812 821
813 822
814 823 # This is only used on Windows.
815 824 def find_job_cmd():
816 825 if WINDOWS:
817 826 try:
818 827 return find_cmd('job')
819 828 except (FindCmdError, ImportError):
820 829 # ImportError will be raised if win32api is not installed
821 830 return 'job'
822 831 else:
823 832 return 'job'
824 833
825 834
826 835 class WindowsHPCLauncher(BaseLauncher):
827 836
828 837 job_id_regexp = CRegExp(r'\d+', config=True,
829 838 help="""A regular expression used to get the job id from the output of the
830 839 submit_command. """
831 840 )
832 841 job_file_name = Unicode(u'ipython_job.xml', config=True,
833 842 help="The filename of the instantiated job script.")
834 843 # The full path to the instantiated job script. This gets made dynamically
835 844 # by combining the work_dir with the job_file_name.
836 845 job_file = Unicode(u'')
837 846 scheduler = Unicode('', config=True,
838 847 help="The hostname of the scheduler to submit the job to.")
839 848 job_cmd = Unicode(find_job_cmd(), config=True,
840 849 help="The command for submitting jobs.")
841 850
842 851 def __init__(self, work_dir=u'.', config=None, **kwargs):
843 852 super(WindowsHPCLauncher, self).__init__(
844 853 work_dir=work_dir, config=config, **kwargs
845 854 )
846 855
847 856 @property
848 857 def job_file(self):
849 858 return os.path.join(self.work_dir, self.job_file_name)
850 859
851 860 def write_job_file(self, n):
852 861 raise NotImplementedError("Implement write_job_file in a subclass.")
853 862
854 863 def find_args(self):
855 864 return [u'job.exe']
856 865
857 866 def parse_job_id(self, output):
858 867 """Take the output of the submit command and return the job id."""
859 868 m = self.job_id_regexp.search(output)
860 869 if m is not None:
861 870 job_id = m.group()
862 871 else:
863 872 raise LauncherError("Job id couldn't be determined: %s" % output)
864 873 self.job_id = job_id
865 874 self.log.info('Job started with id: %r', job_id)
866 875 return job_id
867 876
868 877 def start(self, n):
869 878 """Start n copies of the process using the Win HPC job scheduler."""
870 879 self.write_job_file(n)
871 880 args = [
872 881 'submit',
873 882 '/jobfile:%s' % self.job_file,
874 883 '/scheduler:%s' % self.scheduler
875 884 ]
876 885 self.log.debug("Starting Win HPC Job: %s" % (self.job_cmd + ' ' + ' '.join(args),))
877 886
878 887 output = check_output([self.job_cmd]+args,
879 888 env=os.environ,
880 889 cwd=self.work_dir,
881 890 stderr=STDOUT
882 891 )
883 892 job_id = self.parse_job_id(output)
884 893 self.notify_start(job_id)
885 894 return job_id
886 895
887 896 def stop(self):
888 897 args = [
889 898 'cancel',
890 899 self.job_id,
891 900 '/scheduler:%s' % self.scheduler
892 901 ]
893 902 self.log.info("Stopping Win HPC Job: %s" % (self.job_cmd + ' ' + ' '.join(args),))
894 903 try:
895 904 output = check_output([self.job_cmd]+args,
896 905 env=os.environ,
897 906 cwd=self.work_dir,
898 907 stderr=STDOUT
899 908 )
900 909 except:
901 910 output = 'The job already appears to be stoppped: %r' % self.job_id
902 911 self.notify_stop(dict(job_id=self.job_id, output=output)) # Pass the output of the kill cmd
903 912 return output
904 913
905 914
906 915 class WindowsHPCControllerLauncher(WindowsHPCLauncher, ClusterAppMixin):
907 916
908 917 job_file_name = Unicode(u'ipcontroller_job.xml', config=True,
909 918 help="WinHPC xml job file.")
910 919 controller_args = List([], config=False,
911 920 help="extra args to pass to ipcontroller")
912 921
913 922 def write_job_file(self, n):
914 923 job = IPControllerJob(config=self.config)
915 924
916 925 t = IPControllerTask(config=self.config)
917 926 # The tasks work directory is *not* the actual work directory of
918 927 # the controller. It is used as the base path for the stdout/stderr
919 928 # files that the scheduler redirects to.
920 929 t.work_directory = self.profile_dir
921 930 # Add the profile_dir and from self.start().
922 931 t.controller_args.extend(self.cluster_args)
923 932 t.controller_args.extend(self.controller_args)
924 933 job.add_task(t)
925 934
926 935 self.log.debug("Writing job description file: %s", self.job_file)
927 936 job.write(self.job_file)
928 937
929 938 @property
930 939 def job_file(self):
931 940 return os.path.join(self.profile_dir, self.job_file_name)
932 941
933 942 def start(self):
934 943 """Start the controller by profile_dir."""
935 944 return super(WindowsHPCControllerLauncher, self).start(1)
936 945
937 946
938 947 class WindowsHPCEngineSetLauncher(WindowsHPCLauncher, ClusterAppMixin):
939 948
940 949 job_file_name = Unicode(u'ipengineset_job.xml', config=True,
941 950 help="jobfile for ipengines job")
942 951 engine_args = List([], config=False,
943 952 help="extra args to pas to ipengine")
944 953
945 954 def write_job_file(self, n):
946 955 job = IPEngineSetJob(config=self.config)
947 956
948 957 for i in range(n):
949 958 t = IPEngineTask(config=self.config)
950 959 # The tasks work directory is *not* the actual work directory of
951 960 # the engine. It is used as the base path for the stdout/stderr
952 961 # files that the scheduler redirects to.
953 962 t.work_directory = self.profile_dir
954 963 # Add the profile_dir and from self.start().
955 964 t.engine_args.extend(self.cluster_args)
956 965 t.engine_args.extend(self.engine_args)
957 966 job.add_task(t)
958 967
959 968 self.log.debug("Writing job description file: %s", self.job_file)
960 969 job.write(self.job_file)
961 970
962 971 @property
963 972 def job_file(self):
964 973 return os.path.join(self.profile_dir, self.job_file_name)
965 974
966 975 def start(self, n):
967 976 """Start the controller by profile_dir."""
968 977 return super(WindowsHPCEngineSetLauncher, self).start(n)
969 978
970 979
971 980 #-----------------------------------------------------------------------------
972 981 # Batch (PBS) system launchers
973 982 #-----------------------------------------------------------------------------
974 983
975 984 class BatchClusterAppMixin(ClusterAppMixin):
976 985 """ClusterApp mixin that updates the self.context dict, rather than cl-args."""
977 986 def _profile_dir_changed(self, name, old, new):
978 987 self.context[name] = new
979 988 _cluster_id_changed = _profile_dir_changed
980 989
981 990 def _profile_dir_default(self):
982 991 self.context['profile_dir'] = ''
983 992 return ''
984 993 def _cluster_id_default(self):
985 994 self.context['cluster_id'] = ''
986 995 return ''
987 996
988 997
989 998 class BatchSystemLauncher(BaseLauncher):
990 999 """Launch an external process using a batch system.
991 1000
992 1001 This class is designed to work with UNIX batch systems like PBS, LSF,
993 1002 GridEngine, etc. The overall model is that there are different commands
994 1003 like qsub, qdel, etc. that handle the starting and stopping of the process.
995 1004
996 1005 This class also has the notion of a batch script. The ``batch_template``
997 1006 attribute can be set to a string that is a template for the batch script.
998 1007 This template is instantiated using string formatting. Thus the template can
999 1008 use {n} fot the number of instances. Subclasses can add additional variables
1000 1009 to the template dict.
1001 1010 """
1002 1011
1003 1012 # Subclasses must fill these in. See PBSEngineSet
1004 1013 submit_command = List([''], config=True,
1005 1014 help="The name of the command line program used to submit jobs.")
1006 1015 delete_command = List([''], config=True,
1007 1016 help="The name of the command line program used to delete jobs.")
1008 1017 job_id_regexp = CRegExp('', config=True,
1009 1018 help="""A regular expression used to get the job id from the output of the
1010 1019 submit_command.""")
1011 1020 batch_template = Unicode('', config=True,
1012 1021 help="The string that is the batch script template itself.")
1013 1022 batch_template_file = Unicode(u'', config=True,
1014 1023 help="The file that contains the batch template.")
1015 1024 batch_file_name = Unicode(u'batch_script', config=True,
1016 1025 help="The filename of the instantiated batch script.")
1017 1026 queue = Unicode(u'', config=True,
1018 1027 help="The PBS Queue.")
1019 1028
1020 1029 def _queue_changed(self, name, old, new):
1021 1030 self.context[name] = new
1022 1031
1023 1032 n = Integer(1)
1024 1033 _n_changed = _queue_changed
1025 1034
1026 1035 # not configurable, override in subclasses
1027 1036 # PBS Job Array regex
1028 1037 job_array_regexp = CRegExp('')
1029 1038 job_array_template = Unicode('')
1030 1039 # PBS Queue regex
1031 1040 queue_regexp = CRegExp('')
1032 1041 queue_template = Unicode('')
1033 1042 # The default batch template, override in subclasses
1034 1043 default_template = Unicode('')
1035 1044 # The full path to the instantiated batch script.
1036 1045 batch_file = Unicode(u'')
1037 1046 # the format dict used with batch_template:
1038 1047 context = Dict()
1039 1048 def _context_default(self):
1040 1049 """load the default context with the default values for the basic keys
1041 1050
1042 1051 because the _trait_changed methods only load the context if they
1043 1052 are set to something other than the default value.
1044 1053 """
1045 1054 return dict(n=1, queue=u'', profile_dir=u'', cluster_id=u'')
1046 1055
1047 1056 # the Formatter instance for rendering the templates:
1048 1057 formatter = Instance(EvalFormatter, (), {})
1049 1058
1050 1059
1051 1060 def find_args(self):
1052 1061 return self.submit_command + [self.batch_file]
1053 1062
1054 1063 def __init__(self, work_dir=u'.', config=None, **kwargs):
1055 1064 super(BatchSystemLauncher, self).__init__(
1056 1065 work_dir=work_dir, config=config, **kwargs
1057 1066 )
1058 1067 self.batch_file = os.path.join(self.work_dir, self.batch_file_name)
1059 1068
1060 1069 def parse_job_id(self, output):
1061 1070 """Take the output of the submit command and return the job id."""
1062 1071 m = self.job_id_regexp.search(output)
1063 1072 if m is not None:
1064 1073 job_id = m.group()
1065 1074 else:
1066 1075 raise LauncherError("Job id couldn't be determined: %s" % output)
1067 1076 self.job_id = job_id
1068 1077 self.log.info('Job submitted with job id: %r', job_id)
1069 1078 return job_id
1070 1079
1071 1080 def write_batch_script(self, n):
1072 1081 """Instantiate and write the batch script to the work_dir."""
1073 1082 self.n = n
1074 1083 # first priority is batch_template if set
1075 1084 if self.batch_template_file and not self.batch_template:
1076 1085 # second priority is batch_template_file
1077 1086 with open(self.batch_template_file) as f:
1078 1087 self.batch_template = f.read()
1079 1088 if not self.batch_template:
1080 1089 # third (last) priority is default_template
1081 1090 self.batch_template = self.default_template
1082 1091
1083 1092 # add jobarray or queue lines to user-specified template
1084 1093 # note that this is *only* when user did not specify a template.
1085 1094 # print self.job_array_regexp.search(self.batch_template)
1086 1095 if not self.job_array_regexp.search(self.batch_template):
1087 1096 self.log.debug("adding job array settings to batch script")
1088 1097 firstline, rest = self.batch_template.split('\n',1)
1089 1098 self.batch_template = u'\n'.join([firstline, self.job_array_template, rest])
1090 1099
1091 1100 # print self.queue_regexp.search(self.batch_template)
1092 1101 if self.queue and not self.queue_regexp.search(self.batch_template):
1093 1102 self.log.debug("adding PBS queue settings to batch script")
1094 1103 firstline, rest = self.batch_template.split('\n',1)
1095 1104 self.batch_template = u'\n'.join([firstline, self.queue_template, rest])
1096 1105
1097 1106 script_as_string = self.formatter.format(self.batch_template, **self.context)
1098 1107 self.log.debug('Writing batch script: %s', self.batch_file)
1099 1108
1100 1109 with open(self.batch_file, 'w') as f:
1101 1110 f.write(script_as_string)
1102 1111 os.chmod(self.batch_file, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
1103 1112
1104 1113 def start(self, n):
1105 1114 """Start n copies of the process using a batch system."""
1106 1115 self.log.debug("Starting %s: %r", self.__class__.__name__, self.args)
1107 1116 # Here we save profile_dir in the context so they
1108 1117 # can be used in the batch script template as {profile_dir}
1109 1118 self.write_batch_script(n)
1110 1119 output = check_output(self.args, env=os.environ)
1111 1120
1112 1121 job_id = self.parse_job_id(output)
1113 1122 self.notify_start(job_id)
1114 1123 return job_id
1115 1124
1116 1125 def stop(self):
1117 1126 output = check_output(self.delete_command+[self.job_id], env=os.environ)
1118 1127 self.notify_stop(dict(job_id=self.job_id, output=output)) # Pass the output of the kill cmd
1119 1128 return output
1120 1129
1121 1130
1122 1131 class PBSLauncher(BatchSystemLauncher):
1123 1132 """A BatchSystemLauncher subclass for PBS."""
1124 1133
1125 1134 submit_command = List(['qsub'], config=True,
1126 1135 help="The PBS submit command ['qsub']")
1127 1136 delete_command = List(['qdel'], config=True,
1128 1137 help="The PBS delete command ['qsub']")
1129 1138 job_id_regexp = CRegExp(r'\d+', config=True,
1130 1139 help="Regular expresion for identifying the job ID [r'\d+']")
1131 1140
1132 1141 batch_file = Unicode(u'')
1133 1142 job_array_regexp = CRegExp('#PBS\W+-t\W+[\w\d\-\$]+')
1134 1143 job_array_template = Unicode('#PBS -t 1-{n}')
1135 1144 queue_regexp = CRegExp('#PBS\W+-q\W+\$?\w+')
1136 1145 queue_template = Unicode('#PBS -q {queue}')
1137 1146
1138 1147
1139 1148 class PBSControllerLauncher(PBSLauncher, BatchClusterAppMixin):
1140 1149 """Launch a controller using PBS."""
1141 1150
1142 1151 batch_file_name = Unicode(u'pbs_controller', config=True,
1143 1152 help="batch file name for the controller job.")
1144 1153 default_template= Unicode("""#!/bin/sh
1145 1154 #PBS -V
1146 1155 #PBS -N ipcontroller
1147 1156 %s --log-to-file --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1148 1157 """%(' '.join(map(pipes.quote, ipcontroller_cmd_argv))))
1149 1158
1150 1159
1151 1160 def start(self):
1152 1161 """Start the controller by profile or profile_dir."""
1153 1162 return super(PBSControllerLauncher, self).start(1)
1154 1163
1155 1164
1156 1165 class PBSEngineSetLauncher(PBSLauncher, BatchClusterAppMixin):
1157 1166 """Launch Engines using PBS"""
1158 1167 batch_file_name = Unicode(u'pbs_engines', config=True,
1159 1168 help="batch file name for the engine(s) job.")
1160 1169 default_template= Unicode(u"""#!/bin/sh
1161 1170 #PBS -V
1162 1171 #PBS -N ipengine
1163 1172 %s --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1164 1173 """%(' '.join(map(pipes.quote,ipengine_cmd_argv))))
1165 1174
1166 1175 def start(self, n):
1167 1176 """Start n engines by profile or profile_dir."""
1168 1177 return super(PBSEngineSetLauncher, self).start(n)
1169 1178
1170 1179 #SGE is very similar to PBS
1171 1180
1172 1181 class SGELauncher(PBSLauncher):
1173 1182 """Sun GridEngine is a PBS clone with slightly different syntax"""
1174 1183 job_array_regexp = CRegExp('#\$\W+\-t')
1175 1184 job_array_template = Unicode('#$ -t 1-{n}')
1176 1185 queue_regexp = CRegExp('#\$\W+-q\W+\$?\w+')
1177 1186 queue_template = Unicode('#$ -q {queue}')
1178 1187
1179 1188 class SGEControllerLauncher(SGELauncher, BatchClusterAppMixin):
1180 1189 """Launch a controller using SGE."""
1181 1190
1182 1191 batch_file_name = Unicode(u'sge_controller', config=True,
1183 1192 help="batch file name for the ipontroller job.")
1184 1193 default_template= Unicode(u"""#$ -V
1185 1194 #$ -S /bin/sh
1186 1195 #$ -N ipcontroller
1187 1196 %s --log-to-file --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1188 1197 """%(' '.join(map(pipes.quote, ipcontroller_cmd_argv))))
1189 1198
1190 1199 def start(self):
1191 1200 """Start the controller by profile or profile_dir."""
1192 1201 return super(SGEControllerLauncher, self).start(1)
1193 1202
1194 1203 class SGEEngineSetLauncher(SGELauncher, BatchClusterAppMixin):
1195 1204 """Launch Engines with SGE"""
1196 1205 batch_file_name = Unicode(u'sge_engines', config=True,
1197 1206 help="batch file name for the engine(s) job.")
1198 1207 default_template = Unicode("""#$ -V
1199 1208 #$ -S /bin/sh
1200 1209 #$ -N ipengine
1201 1210 %s --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1202 1211 """%(' '.join(map(pipes.quote, ipengine_cmd_argv))))
1203 1212
1204 1213 def start(self, n):
1205 1214 """Start n engines by profile or profile_dir."""
1206 1215 return super(SGEEngineSetLauncher, self).start(n)
1207 1216
1208 1217
1209 1218 # LSF launchers
1210 1219
1211 1220 class LSFLauncher(BatchSystemLauncher):
1212 1221 """A BatchSystemLauncher subclass for LSF."""
1213 1222
1214 1223 submit_command = List(['bsub'], config=True,
1215 1224 help="The PBS submit command ['bsub']")
1216 1225 delete_command = List(['bkill'], config=True,
1217 1226 help="The PBS delete command ['bkill']")
1218 1227 job_id_regexp = CRegExp(r'\d+', config=True,
1219 1228 help="Regular expresion for identifying the job ID [r'\d+']")
1220 1229
1221 1230 batch_file = Unicode(u'')
1222 1231 job_array_regexp = CRegExp('#BSUB[ \t]-J+\w+\[\d+-\d+\]')
1223 1232 job_array_template = Unicode('#BSUB -J ipengine[1-{n}]')
1224 1233 queue_regexp = CRegExp('#BSUB[ \t]+-q[ \t]+\w+')
1225 1234 queue_template = Unicode('#BSUB -q {queue}')
1226 1235
1227 1236 def start(self, n):
1228 1237 """Start n copies of the process using LSF batch system.
1229 1238 This cant inherit from the base class because bsub expects
1230 1239 to be piped a shell script in order to honor the #BSUB directives :
1231 1240 bsub < script
1232 1241 """
1233 1242 # Here we save profile_dir in the context so they
1234 1243 # can be used in the batch script template as {profile_dir}
1235 1244 self.write_batch_script(n)
1236 1245 #output = check_output(self.args, env=os.environ)
1237 1246 piped_cmd = self.args[0]+'<\"'+self.args[1]+'\"'
1238 1247 self.log.debug("Starting %s: %s", self.__class__.__name__, piped_cmd)
1239 1248 p = Popen(piped_cmd, shell=True,env=os.environ,stdout=PIPE)
1240 1249 output,err = p.communicate()
1241 1250 job_id = self.parse_job_id(output)
1242 1251 self.notify_start(job_id)
1243 1252 return job_id
1244 1253
1245 1254
1246 1255 class LSFControllerLauncher(LSFLauncher, BatchClusterAppMixin):
1247 1256 """Launch a controller using LSF."""
1248 1257
1249 1258 batch_file_name = Unicode(u'lsf_controller', config=True,
1250 1259 help="batch file name for the controller job.")
1251 1260 default_template= Unicode("""#!/bin/sh
1252 1261 #BSUB -J ipcontroller
1253 1262 #BSUB -oo ipcontroller.o.%%J
1254 1263 #BSUB -eo ipcontroller.e.%%J
1255 1264 %s --log-to-file --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1256 1265 """%(' '.join(map(pipes.quote,ipcontroller_cmd_argv))))
1257 1266
1258 1267 def start(self):
1259 1268 """Start the controller by profile or profile_dir."""
1260 1269 return super(LSFControllerLauncher, self).start(1)
1261 1270
1262 1271
1263 1272 class LSFEngineSetLauncher(LSFLauncher, BatchClusterAppMixin):
1264 1273 """Launch Engines using LSF"""
1265 1274 batch_file_name = Unicode(u'lsf_engines', config=True,
1266 1275 help="batch file name for the engine(s) job.")
1267 1276 default_template= Unicode(u"""#!/bin/sh
1268 1277 #BSUB -oo ipengine.o.%%J
1269 1278 #BSUB -eo ipengine.e.%%J
1270 1279 %s --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1271 1280 """%(' '.join(map(pipes.quote, ipengine_cmd_argv))))
1272 1281
1273 1282 def start(self, n):
1274 1283 """Start n engines by profile or profile_dir."""
1275 1284 return super(LSFEngineSetLauncher, self).start(n)
1276 1285
1277 1286
1278 1287 #-----------------------------------------------------------------------------
1279 1288 # A launcher for ipcluster itself!
1280 1289 #-----------------------------------------------------------------------------
1281 1290
1282 1291
1283 1292 class IPClusterLauncher(LocalProcessLauncher):
1284 1293 """Launch the ipcluster program in an external process."""
1285 1294
1286 1295 ipcluster_cmd = List(ipcluster_cmd_argv, config=True,
1287 1296 help="Popen command for ipcluster")
1288 1297 ipcluster_args = List(
1289 1298 ['--clean-logs=True', '--log-to-file', '--log-level=%i'%logging.INFO], config=True,
1290 1299 help="Command line arguments to pass to ipcluster.")
1291 1300 ipcluster_subcommand = Unicode('start')
1292 1301 profile = Unicode('default')
1293 1302 n = Integer(2)
1294 1303
1295 1304 def find_args(self):
1296 1305 return self.ipcluster_cmd + [self.ipcluster_subcommand] + \
1297 1306 ['--n=%i'%self.n, '--profile=%s'%self.profile] + \
1298 1307 self.ipcluster_args
1299 1308
1300 1309 def start(self):
1301 1310 return super(IPClusterLauncher, self).start()
1302 1311
1303 1312 #-----------------------------------------------------------------------------
1304 1313 # Collections of launchers
1305 1314 #-----------------------------------------------------------------------------
1306 1315
1307 1316 local_launchers = [
1308 1317 LocalControllerLauncher,
1309 1318 LocalEngineLauncher,
1310 1319 LocalEngineSetLauncher,
1311 1320 ]
1312 1321 mpi_launchers = [
1313 1322 MPILauncher,
1314 1323 MPIControllerLauncher,
1315 1324 MPIEngineSetLauncher,
1316 1325 ]
1317 1326 ssh_launchers = [
1318 1327 SSHLauncher,
1319 1328 SSHControllerLauncher,
1320 1329 SSHEngineLauncher,
1321 1330 SSHEngineSetLauncher,
1322 1331 ]
1323 1332 winhpc_launchers = [
1324 1333 WindowsHPCLauncher,
1325 1334 WindowsHPCControllerLauncher,
1326 1335 WindowsHPCEngineSetLauncher,
1327 1336 ]
1328 1337 pbs_launchers = [
1329 1338 PBSLauncher,
1330 1339 PBSControllerLauncher,
1331 1340 PBSEngineSetLauncher,
1332 1341 ]
1333 1342 sge_launchers = [
1334 1343 SGELauncher,
1335 1344 SGEControllerLauncher,
1336 1345 SGEEngineSetLauncher,
1337 1346 ]
1338 1347 lsf_launchers = [
1339 1348 LSFLauncher,
1340 1349 LSFControllerLauncher,
1341 1350 LSFEngineSetLauncher,
1342 1351 ]
1343 1352 all_launchers = local_launchers + mpi_launchers + ssh_launchers + winhpc_launchers\
1344 1353 + pbs_launchers + sge_launchers + lsf_launchers
1345 1354
@@ -1,491 +1,494 b''
1 1 # encoding: utf-8
2 2 """
3 3 Utilities for path handling.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2011 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 import os
18 18 import sys
19 19 import tempfile
20 20 import warnings
21 21 from hashlib import md5
22 22 import glob
23 23
24 24 import IPython
25 25 from IPython.testing.skipdoctest import skip_doctest
26 26 from IPython.utils.process import system
27 27 from IPython.utils.importstring import import_item
28 28 from IPython.utils import py3compat
29 29 #-----------------------------------------------------------------------------
30 30 # Code
31 31 #-----------------------------------------------------------------------------
32 32
33 33 fs_encoding = sys.getfilesystemencoding()
34 34
35 35 def _get_long_path_name(path):
36 36 """Dummy no-op."""
37 37 return path
38 38
39 39 def _writable_dir(path):
40 40 """Whether `path` is a directory, to which the user has write access."""
41 41 return os.path.isdir(path) and os.access(path, os.W_OK)
42 42
43 43 if sys.platform == 'win32':
44 44 @skip_doctest
45 45 def _get_long_path_name(path):
46 46 """Get a long path name (expand ~) on Windows using ctypes.
47 47
48 48 Examples
49 49 --------
50 50
51 51 >>> get_long_path_name('c:\\docume~1')
52 52 u'c:\\\\Documents and Settings'
53 53
54 54 """
55 55 try:
56 56 import ctypes
57 57 except ImportError:
58 58 raise ImportError('you need to have ctypes installed for this to work')
59 59 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
60 60 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
61 61 ctypes.c_uint ]
62 62
63 63 buf = ctypes.create_unicode_buffer(260)
64 64 rv = _GetLongPathName(path, buf, 260)
65 65 if rv == 0 or rv > 260:
66 66 return path
67 67 else:
68 68 return buf.value
69 69
70 70
71 71 def get_long_path_name(path):
72 72 """Expand a path into its long form.
73 73
74 74 On Windows this expands any ~ in the paths. On other platforms, it is
75 75 a null operation.
76 76 """
77 77 return _get_long_path_name(path)
78 78
79 79
80 80 def unquote_filename(name, win32=(sys.platform=='win32')):
81 81 """ On Windows, remove leading and trailing quotes from filenames.
82 82 """
83 83 if win32:
84 84 if name.startswith(("'", '"')) and name.endswith(("'", '"')):
85 85 name = name[1:-1]
86 86 return name
87 87
88 88
89 89 def get_py_filename(name, force_win32=None):
90 90 """Return a valid python filename in the current directory.
91 91
92 92 If the given name is not a file, it adds '.py' and searches again.
93 93 Raises IOError with an informative message if the file isn't found.
94 94
95 95 On Windows, apply Windows semantics to the filename. In particular, remove
96 96 any quoting that has been applied to it. This option can be forced for
97 97 testing purposes.
98 98 """
99 99
100 100 name = os.path.expanduser(name)
101 101 if force_win32 is None:
102 102 win32 = (sys.platform == 'win32')
103 103 else:
104 104 win32 = force_win32
105 105 name = unquote_filename(name, win32=win32)
106 106 if not os.path.isfile(name) and not name.endswith('.py'):
107 107 name += '.py'
108 108 if os.path.isfile(name):
109 109 return name
110 110 else:
111 111 raise IOError('File `%r` not found.' % name)
112 112
113 113
114 114 def filefind(filename, path_dirs=None):
115 115 """Find a file by looking through a sequence of paths.
116 116
117 117 This iterates through a sequence of paths looking for a file and returns
118 118 the full, absolute path of the first occurence of the file. If no set of
119 119 path dirs is given, the filename is tested as is, after running through
120 120 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
121 121
122 122 filefind('myfile.txt')
123 123
124 124 will find the file in the current working dir, but::
125 125
126 126 filefind('~/myfile.txt')
127 127
128 128 Will find the file in the users home directory. This function does not
129 129 automatically try any paths, such as the cwd or the user's home directory.
130 130
131 131 Parameters
132 132 ----------
133 133 filename : str
134 134 The filename to look for.
135 135 path_dirs : str, None or sequence of str
136 136 The sequence of paths to look for the file in. If None, the filename
137 137 need to be absolute or be in the cwd. If a string, the string is
138 138 put into a sequence and the searched. If a sequence, walk through
139 139 each element and join with ``filename``, calling :func:`expandvars`
140 140 and :func:`expanduser` before testing for existence.
141 141
142 142 Returns
143 143 -------
144 144 Raises :exc:`IOError` or returns absolute path to file.
145 145 """
146 146
147 147 # If paths are quoted, abspath gets confused, strip them...
148 148 filename = filename.strip('"').strip("'")
149 149 # If the input is an absolute path, just check it exists
150 150 if os.path.isabs(filename) and os.path.isfile(filename):
151 151 return filename
152 152
153 153 if path_dirs is None:
154 154 path_dirs = ("",)
155 155 elif isinstance(path_dirs, basestring):
156 156 path_dirs = (path_dirs,)
157 157
158 158 for path in path_dirs:
159 159 if path == '.': path = os.getcwdu()
160 160 testname = expand_path(os.path.join(path, filename))
161 161 if os.path.isfile(testname):
162 162 return os.path.abspath(testname)
163 163
164 164 raise IOError("File %r does not exist in any of the search paths: %r" %
165 165 (filename, path_dirs) )
166 166
167 167
168 168 class HomeDirError(Exception):
169 169 pass
170 170
171 171
172 172 def get_home_dir(require_writable=False):
173 173 """Return the 'home' directory, as a unicode string.
174 174
175 175 * First, check for frozen env in case of py2exe
176 176 * Otherwise, defer to os.path.expanduser('~')
177 177
178 178 See stdlib docs for how this is determined.
179 179 $HOME is first priority on *ALL* platforms.
180 180
181 181 Parameters
182 182 ----------
183 183
184 184 require_writable : bool [default: False]
185 185 if True:
186 186 guarantees the return value is a writable directory, otherwise
187 187 raises HomeDirError
188 188 if False:
189 189 The path is resolved, but it is not guaranteed to exist or be writable.
190 190 """
191 191
192 192 # first, check py2exe distribution root directory for _ipython.
193 193 # This overrides all. Normally does not exist.
194 194
195 195 if hasattr(sys, "frozen"): #Is frozen by py2exe
196 196 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
197 197 root, rest = IPython.__file__.lower().split('library.zip')
198 198 else:
199 199 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
200 200 root=os.path.abspath(root).rstrip('\\')
201 201 if _writable_dir(os.path.join(root, '_ipython')):
202 202 os.environ["IPYKITROOT"] = root
203 203 return py3compat.cast_unicode(root, fs_encoding)
204 204
205 205 homedir = os.path.expanduser('~')
206 206 # Next line will make things work even when /home/ is a symlink to
207 207 # /usr/home as it is on FreeBSD, for example
208 208 homedir = os.path.realpath(homedir)
209 209
210 210 if not _writable_dir(homedir) and os.name == 'nt':
211 211 # expanduser failed, use the registry to get the 'My Documents' folder.
212 212 try:
213 213 import _winreg as wreg
214 214 key = wreg.OpenKey(
215 215 wreg.HKEY_CURRENT_USER,
216 216 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
217 217 )
218 218 homedir = wreg.QueryValueEx(key,'Personal')[0]
219 219 key.Close()
220 220 except:
221 221 pass
222 222
223 223 if (not require_writable) or _writable_dir(homedir):
224 224 return py3compat.cast_unicode(homedir, fs_encoding)
225 225 else:
226 226 raise HomeDirError('%s is not a writable dir, '
227 227 'set $HOME environment variable to override' % homedir)
228 228
229 229 def get_xdg_dir():
230 230 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
231 231
232 232 This is only for non-OS X posix (Linux,Unix,etc.) systems.
233 233 """
234 234
235 235 env = os.environ
236 236
237 237 if os.name == 'posix' and sys.platform != 'darwin':
238 238 # Linux, Unix, AIX, etc.
239 239 # use ~/.config if empty OR not set
240 240 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
241 241 if xdg and _writable_dir(xdg):
242 242 return py3compat.cast_unicode(xdg, fs_encoding)
243 243
244 244 return None
245 245
246 246
247 247 def get_ipython_dir():
248 248 """Get the IPython directory for this platform and user.
249 249
250 250 This uses the logic in `get_home_dir` to find the home directory
251 251 and then adds .ipython to the end of the path.
252 252 """
253 253
254 254 env = os.environ
255 255 pjoin = os.path.join
256 256
257 257
258 258 ipdir_def = '.ipython'
259 259 xdg_def = 'ipython'
260 260
261 261 home_dir = get_home_dir()
262 262 xdg_dir = get_xdg_dir()
263 263
264 264 # import pdb; pdb.set_trace() # dbg
265 265 if 'IPYTHON_DIR' in env:
266 266 warnings.warn('The environment variable IPYTHON_DIR is deprecated. '
267 267 'Please use IPYTHONDIR instead.')
268 268 ipdir = env.get('IPYTHONDIR', env.get('IPYTHON_DIR', None))
269 269 if ipdir is None:
270 270 # not set explicitly, use XDG_CONFIG_HOME or HOME
271 271 home_ipdir = pjoin(home_dir, ipdir_def)
272 272 if xdg_dir:
273 273 # use XDG, as long as the user isn't already
274 274 # using $HOME/.ipython and *not* XDG/ipython
275 275
276 276 xdg_ipdir = pjoin(xdg_dir, xdg_def)
277 277
278 278 if _writable_dir(xdg_ipdir) or not _writable_dir(home_ipdir):
279 279 ipdir = xdg_ipdir
280 280
281 281 if ipdir is None:
282 282 # not using XDG
283 283 ipdir = home_ipdir
284 284
285 285 ipdir = os.path.normpath(os.path.expanduser(ipdir))
286 286
287 287 if os.path.exists(ipdir) and not _writable_dir(ipdir):
288 288 # ipdir exists, but is not writable
289 289 warnings.warn("IPython dir '%s' is not a writable location,"
290 290 " using a temp directory."%ipdir)
291 291 ipdir = tempfile.mkdtemp()
292 292 elif not os.path.exists(ipdir):
293 293 parent = ipdir.rsplit(os.path.sep, 1)[0]
294 294 if not _writable_dir(parent):
295 295 # ipdir does not exist and parent isn't writable
296 296 warnings.warn("IPython parent '%s' is not a writable location,"
297 297 " using a temp directory."%parent)
298 298 ipdir = tempfile.mkdtemp()
299 299
300 300 return py3compat.cast_unicode(ipdir, fs_encoding)
301 301
302 302
303 303 def get_ipython_package_dir():
304 304 """Get the base directory where IPython itself is installed."""
305 305 ipdir = os.path.dirname(IPython.__file__)
306 306 return py3compat.cast_unicode(ipdir, fs_encoding)
307 307
308 308
309 309 def get_ipython_module_path(module_str):
310 310 """Find the path to an IPython module in this version of IPython.
311 311
312 312 This will always find the version of the module that is in this importable
313 313 IPython package. This will always return the path to the ``.py``
314 314 version of the module.
315 315 """
316 316 if module_str == 'IPython':
317 317 return os.path.join(get_ipython_package_dir(), '__init__.py')
318 318 mod = import_item(module_str)
319 319 the_path = mod.__file__.replace('.pyc', '.py')
320 320 the_path = the_path.replace('.pyo', '.py')
321 321 return py3compat.cast_unicode(the_path, fs_encoding)
322 322
323 323 def locate_profile(profile='default'):
324 324 """Find the path to the folder associated with a given profile.
325 325
326 326 I.e. find $IPYTHONDIR/profile_whatever.
327 327 """
328 328 from IPython.core.profiledir import ProfileDir, ProfileDirError
329 329 try:
330 330 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
331 331 except ProfileDirError:
332 332 # IOError makes more sense when people are expecting a path
333 333 raise IOError("Couldn't find profile %r" % profile)
334 334 return pd.location
335 335
336 336 def expand_path(s):
337 337 """Expand $VARS and ~names in a string, like a shell
338 338
339 339 :Examples:
340 340
341 341 In [2]: os.environ['FOO']='test'
342 342
343 343 In [3]: expand_path('variable FOO is $FOO')
344 344 Out[3]: 'variable FOO is test'
345 345 """
346 346 # This is a pretty subtle hack. When expand user is given a UNC path
347 347 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
348 348 # the $ to get (\\server\share\%username%). I think it considered $
349 349 # alone an empty var. But, we need the $ to remains there (it indicates
350 350 # a hidden share).
351 351 if os.name=='nt':
352 352 s = s.replace('$\\', 'IPYTHON_TEMP')
353 353 s = os.path.expandvars(os.path.expanduser(s))
354 354 if os.name=='nt':
355 355 s = s.replace('IPYTHON_TEMP', '$\\')
356 356 return s
357 357
358 358
359 359 def unescape_glob(string):
360 360 """Unescape glob pattern in `string`."""
361 361 def unescape(s):
362 362 for pattern in '*[]!?':
363 363 s = s.replace(r'\{0}'.format(pattern), pattern)
364 364 return s
365 365 return '\\'.join(map(unescape, string.split('\\\\')))
366 366
367 367
368 368 def shellglob(args):
369 369 """
370 370 Do glob expansion for each element in `args` and return a flattened list.
371 371
372 372 Unmatched glob pattern will remain as-is in the returned list.
373 373
374 374 """
375 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 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 381 return expanded
379 382
380 383
381 384 def target_outdated(target,deps):
382 385 """Determine whether a target is out of date.
383 386
384 387 target_outdated(target,deps) -> 1/0
385 388
386 389 deps: list of filenames which MUST exist.
387 390 target: single filename which may or may not exist.
388 391
389 392 If target doesn't exist or is older than any file listed in deps, return
390 393 true, otherwise return false.
391 394 """
392 395 try:
393 396 target_time = os.path.getmtime(target)
394 397 except os.error:
395 398 return 1
396 399 for dep in deps:
397 400 dep_time = os.path.getmtime(dep)
398 401 if dep_time > target_time:
399 402 #print "For target",target,"Dep failed:",dep # dbg
400 403 #print "times (dep,tar):",dep_time,target_time # dbg
401 404 return 1
402 405 return 0
403 406
404 407
405 408 def target_update(target,deps,cmd):
406 409 """Update a target with a given command given a list of dependencies.
407 410
408 411 target_update(target,deps,cmd) -> runs cmd if target is outdated.
409 412
410 413 This is just a wrapper around target_outdated() which calls the given
411 414 command if target is outdated."""
412 415
413 416 if target_outdated(target,deps):
414 417 system(cmd)
415 418
416 419 def filehash(path):
417 420 """Make an MD5 hash of a file, ignoring any differences in line
418 421 ending characters."""
419 422 with open(path, "rU") as f:
420 423 return md5(py3compat.str_to_bytes(f.read())).hexdigest()
421 424
422 425 # If the config is unmodified from the default, we'll just delete it.
423 426 # These are consistent for 0.10.x, thankfully. We're not going to worry about
424 427 # older versions.
425 428 old_config_md5 = {'ipy_user_conf.py': 'fc108bedff4b9a00f91fa0a5999140d3',
426 429 'ipythonrc': '12a68954f3403eea2eec09dc8fe5a9b5'}
427 430
428 431 def check_for_old_config(ipython_dir=None):
429 432 """Check for old config files, and present a warning if they exist.
430 433
431 434 A link to the docs of the new config is included in the message.
432 435
433 436 This should mitigate confusion with the transition to the new
434 437 config system in 0.11.
435 438 """
436 439 if ipython_dir is None:
437 440 ipython_dir = get_ipython_dir()
438 441
439 442 old_configs = ['ipy_user_conf.py', 'ipythonrc', 'ipython_config.py']
440 443 warned = False
441 444 for cfg in old_configs:
442 445 f = os.path.join(ipython_dir, cfg)
443 446 if os.path.exists(f):
444 447 if filehash(f) == old_config_md5.get(cfg, ''):
445 448 os.unlink(f)
446 449 else:
447 450 warnings.warn("Found old IPython config file %r (modified by user)"%f)
448 451 warned = True
449 452
450 453 if warned:
451 454 warnings.warn("""
452 455 The IPython configuration system has changed as of 0.11, and these files will
453 456 be ignored. See http://ipython.github.com/ipython-doc/dev/config for details
454 457 of the new config system.
455 458 To start configuring IPython, do `ipython profile create`, and edit
456 459 `ipython_config.py` in <ipython_dir>/profile_default.
457 460 If you need to leave the old config files in place for an older version of
458 461 IPython and want to suppress this warning message, set
459 462 `c.InteractiveShellApp.ignore_old_config=True` in the new config.""")
460 463
461 464 def get_security_file(filename, profile='default'):
462 465 """Return the absolute path of a security file given by filename and profile
463 466
464 467 This allows users and developers to find security files without
465 468 knowledge of the IPython directory structure. The search path
466 469 will be ['.', profile.security_dir]
467 470
468 471 Parameters
469 472 ----------
470 473
471 474 filename : str
472 475 The file to be found. If it is passed as an absolute path, it will
473 476 simply be returned.
474 477 profile : str [default: 'default']
475 478 The name of the profile to search. Leaving this unspecified
476 479 The file to be found. If it is passed as an absolute path, fname will
477 480 simply be returned.
478 481
479 482 Returns
480 483 -------
481 484 Raises :exc:`IOError` if file not found or returns absolute path to file.
482 485 """
483 486 # import here, because profiledir also imports from utils.path
484 487 from IPython.core.profiledir import ProfileDir
485 488 try:
486 489 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
487 490 except Exception:
488 491 # will raise ProfileDirError if no such profile
489 492 raise IOError("Profile %r not found")
490 493 return filefind(filename, ['.', pd.security_dir])
491 494
@@ -1,124 +1,124 b''
1 1 # -*- coding: utf-8 -*-
2 2 """ Imports and provides the 'correct' version of readline for the platform.
3 3
4 4 Readline is used throughout IPython as::
5 5
6 6 import IPython.utils.rlineimpl as readline
7 7
8 8 In addition to normal readline stuff, this module provides have_readline
9 9 boolean and _outputfile variable used in IPython.utils.
10 10 """
11 11
12 12 import os
13 13 import re
14 14 import sys
15 15 import time
16 16 import warnings
17 17
18 18 from subprocess import Popen, PIPE
19 19
20 20 if sys.platform == 'darwin':
21 21 # dirty trick, to skip the system readline, because pip-installed readline
22 22 # will never be found on OSX, since lib-dynload always comes ahead of site-packages
23 23 from distutils import sysconfig
24 24 lib_dynload = sysconfig.get_config_var('DESTSHARED')
25 25 del sysconfig
26 26 try:
27 27 dynload_idx = sys.path.index(lib_dynload)
28 28 except ValueError:
29 29 dynload_idx = None
30 30 else:
31 31 sys.path.pop(dynload_idx)
32 32 try:
33 33 from readline import *
34 34 import readline as _rl
35 35 have_readline = True
36 36 except ImportError:
37 37 try:
38 38 from pyreadline import *
39 39 import pyreadline as _rl
40 40 have_readline = True
41 41 except ImportError:
42 42 have_readline = False
43 43
44 44 if sys.platform == 'darwin':
45 45 # dirty trick, part II:
46 46 if dynload_idx is not None:
47 47 # restore path
48 48 sys.path.insert(dynload_idx, lib_dynload)
49 49 if not have_readline:
50 50 # *only* have system readline, try import again
51 51 try:
52 52 from readline import *
53 53 import readline as _rl
54 54 have_readline = True
55 55 except ImportError:
56 56 have_readline = False
57 57 else:
58 58 # if we want to warn about EPD / Fink having bad readline
59 59 # we would do it here
60 60 pass
61 61 # cleanup dirty trick vars
62 62 del dynload_idx, lib_dynload
63 63
64 64 if have_readline and hasattr(_rl, 'rlmain'):
65 65 # patch add_history to allow for strings in pyreadline <= 1.5:
66 66 # fix copied from pyreadline 1.6
67 67 import pyreadline
68 68 if pyreadline.release.version <= '1.5':
69 69 def add_history(line):
70 70 """add a line to the history buffer."""
71 71 from pyreadline import lineobj
72 72 if not isinstance(line, lineobj.TextLine):
73 73 line = lineobj.TextLine(line)
74 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 77 try:
78 78 _outputfile=_rl.GetOutputFile()
79 79 except AttributeError:
80 80 warnings.warn("Failed GetOutputFile")
81 81 have_readline = False
82 82
83 83 # Test to see if libedit is being used instead of GNU readline.
84 84 # Thanks to Boyd Waters for the original patch.
85 85 uses_libedit = False
86 86
87 87 if have_readline:
88 88 # Official Python docs state that 'libedit' is in the docstring for libedit readline:
89 89 uses_libedit = _rl.__doc__ and 'libedit' in _rl.__doc__
90 90 # Note that many non-System Pythons also do not use proper readline,
91 91 # but do not report libedit at all, nor are they linked dynamically against libedit.
92 92 # known culprits of this include: EPD, Fink
93 93 # There is not much we can do to detect this, until we find a specific failure
94 94 # case, rather than relying on the readline module to self-identify as broken.
95 95
96 96 if uses_libedit and sys.platform == 'darwin':
97 97 _rl.parse_and_bind("bind ^I rl_complete")
98 98 warnings.warn('\n'.join(['', "*"*78,
99 99 "libedit detected - readline will not be well behaved, including but not limited to:",
100 100 " * crashes on tab completion",
101 101 " * incorrect history navigation",
102 102 " * corrupting long-lines",
103 103 " * failure to wrap or indent lines properly",
104 104 "It is highly recommended that you install readline, which is easy_installable:",
105 105 " easy_install readline",
106 106 "Note that `pip install readline` generally DOES NOT WORK, because",
107 107 "it installs to site-packages, which come *after* lib-dynload in sys.path,",
108 108 "where readline is located. It must be `easy_install readline`, or to a custom",
109 109 "location on your PYTHONPATH (even --user comes after lib-dyload).",
110 110 "*"*78]),
111 111 RuntimeWarning)
112 112
113 113 # the clear_history() function was only introduced in Python 2.4 and is
114 114 # actually optional in the readline API, so we must explicitly check for its
115 115 # existence. Some known platforms actually don't have it. This thread:
116 116 # http://mail.python.org/pipermail/python-dev/2003-August/037845.html
117 117 # has the original discussion.
118 118
119 119 if have_readline:
120 120 try:
121 121 _rl.clear_history
122 122 except AttributeError:
123 123 def clear_history(): pass
124 124 _rl.clear_history = clear_history
@@ -1,492 +1,524 b''
1 1 # encoding: utf-8
2 2 """Tests for IPython.utils.path.py"""
3 3
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (C) 2008-2011 The IPython Development Team
6 6 #
7 7 # Distributed under the terms of the BSD License. The full license is in
8 8 # the file COPYING, distributed as part of this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 from __future__ import with_statement
16 16
17 17 import os
18 18 import shutil
19 19 import sys
20 20 import tempfile
21 21 from io import StringIO
22 from contextlib import contextmanager
22 23
23 24 from os.path import join, abspath, split
24 25
25 26 import nose.tools as nt
26 27
27 28 from nose import with_setup
28 29
29 30 import IPython
30 31 from IPython.testing import decorators as dec
31 32 from IPython.testing.decorators import skip_if_not_win32, skip_win32
32 33 from IPython.testing.tools import make_tempfile, AssertPrints
33 34 from IPython.utils import path, io
34 35 from IPython.utils import py3compat
35 36 from IPython.utils.tempdir import TemporaryDirectory
36 37
37 38 # Platform-dependent imports
38 39 try:
39 40 import _winreg as wreg
40 41 except ImportError:
41 42 #Fake _winreg module on none windows platforms
42 43 import types
43 44 wr_name = "winreg" if py3compat.PY3 else "_winreg"
44 45 sys.modules[wr_name] = types.ModuleType(wr_name)
45 46 import _winreg as wreg
46 47 #Add entries that needs to be stubbed by the testing code
47 48 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
48 49
49 50 try:
50 51 reload
51 52 except NameError: # Python 3
52 53 from imp import reload
53 54
54 55 #-----------------------------------------------------------------------------
55 56 # Globals
56 57 #-----------------------------------------------------------------------------
57 58 env = os.environ
58 59 TEST_FILE_PATH = split(abspath(__file__))[0]
59 60 TMP_TEST_DIR = tempfile.mkdtemp()
60 61 HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")
61 62 XDG_TEST_DIR = join(HOME_TEST_DIR, "xdg_test_dir")
62 63 IP_TEST_DIR = join(HOME_TEST_DIR,'.ipython')
63 64 #
64 65 # Setup/teardown functions/decorators
65 66 #
66 67
67 68 def setup():
68 69 """Setup testenvironment for the module:
69 70
70 71 - Adds dummy home dir tree
71 72 """
72 73 # Do not mask exceptions here. In particular, catching WindowsError is a
73 74 # problem because that exception is only defined on Windows...
74 75 os.makedirs(IP_TEST_DIR)
75 76 os.makedirs(os.path.join(XDG_TEST_DIR, 'ipython'))
76 77
77 78
78 79 def teardown():
79 80 """Teardown testenvironment for the module:
80 81
81 82 - Remove dummy home dir tree
82 83 """
83 84 # Note: we remove the parent test dir, which is the root of all test
84 85 # subdirs we may have created. Use shutil instead of os.removedirs, so
85 86 # that non-empty directories are all recursively removed.
86 87 shutil.rmtree(TMP_TEST_DIR)
87 88
88 89
89 90 def setup_environment():
90 91 """Setup testenvironment for some functions that are tested
91 92 in this module. In particular this functions stores attributes
92 93 and other things that we need to stub in some test functions.
93 94 This needs to be done on a function level and not module level because
94 95 each testfunction needs a pristine environment.
95 96 """
96 97 global oldstuff, platformstuff
97 98 oldstuff = (env.copy(), os.name, sys.platform, path.get_home_dir, IPython.__file__, os.getcwd())
98 99
99 100 if os.name == 'nt':
100 101 platformstuff = (wreg.OpenKey, wreg.QueryValueEx,)
101 102
102 103
103 104 def teardown_environment():
104 105 """Restore things that were remebered by the setup_environment function
105 106 """
106 107 (oldenv, os.name, sys.platform, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
107 108 os.chdir(old_wd)
108 109 reload(path)
109 110
110 111 for key in env.keys():
111 112 if key not in oldenv:
112 113 del env[key]
113 114 env.update(oldenv)
114 115 if hasattr(sys, 'frozen'):
115 116 del sys.frozen
116 117 if os.name == 'nt':
117 118 (wreg.OpenKey, wreg.QueryValueEx,) = platformstuff
118 119
119 120 # Build decorator that uses the setup_environment/setup_environment
120 121 with_environment = with_setup(setup_environment, teardown_environment)
121 122
122 123 @skip_if_not_win32
123 124 @with_environment
124 125 def test_get_home_dir_1():
125 126 """Testcase for py2exe logic, un-compressed lib
126 127 """
127 128 sys.frozen = True
128 129
129 130 #fake filename for IPython.__init__
130 131 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
131 132
132 133 home_dir = path.get_home_dir()
133 134 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
134 135
135 136
136 137 @skip_if_not_win32
137 138 @with_environment
138 139 def test_get_home_dir_2():
139 140 """Testcase for py2exe logic, compressed lib
140 141 """
141 142 sys.frozen = True
142 143 #fake filename for IPython.__init__
143 144 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
144 145
145 146 home_dir = path.get_home_dir(True)
146 147 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR).lower())
147 148
148 149
149 150 @with_environment
150 151 def test_get_home_dir_3():
151 152 """get_home_dir() uses $HOME if set"""
152 153 env["HOME"] = HOME_TEST_DIR
153 154 home_dir = path.get_home_dir(True)
154 155 # get_home_dir expands symlinks
155 156 nt.assert_equal(home_dir, os.path.realpath(env["HOME"]))
156 157
157 158
158 159 @with_environment
159 160 def test_get_home_dir_4():
160 161 """get_home_dir() still works if $HOME is not set"""
161 162
162 163 if 'HOME' in env: del env['HOME']
163 164 # this should still succeed, but we don't care what the answer is
164 165 home = path.get_home_dir(False)
165 166
166 167 @with_environment
167 168 def test_get_home_dir_5():
168 169 """raise HomeDirError if $HOME is specified, but not a writable dir"""
169 170 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
170 171 # set os.name = posix, to prevent My Documents fallback on Windows
171 172 os.name = 'posix'
172 173 nt.assert_raises(path.HomeDirError, path.get_home_dir, True)
173 174
174 175
175 176 # Should we stub wreg fully so we can run the test on all platforms?
176 177 @skip_if_not_win32
177 178 @with_environment
178 179 def test_get_home_dir_8():
179 180 """Using registry hack for 'My Documents', os=='nt'
180 181
181 182 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
182 183 """
183 184 os.name = 'nt'
184 185 # Remove from stub environment all keys that may be set
185 186 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
186 187 env.pop(key, None)
187 188
188 189 #Stub windows registry functions
189 190 def OpenKey(x, y):
190 191 class key:
191 192 def Close(self):
192 193 pass
193 194 return key()
194 195 def QueryValueEx(x, y):
195 196 return [abspath(HOME_TEST_DIR)]
196 197
197 198 wreg.OpenKey = OpenKey
198 199 wreg.QueryValueEx = QueryValueEx
199 200
200 201 home_dir = path.get_home_dir()
201 202 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
202 203
203 204
204 205 @with_environment
205 206 def test_get_ipython_dir_1():
206 207 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
207 208 env_ipdir = os.path.join("someplace", ".ipython")
208 209 path._writable_dir = lambda path: True
209 210 env['IPYTHONDIR'] = env_ipdir
210 211 ipdir = path.get_ipython_dir()
211 212 nt.assert_equal(ipdir, env_ipdir)
212 213
213 214
214 215 @with_environment
215 216 def test_get_ipython_dir_2():
216 217 """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
217 218 path.get_home_dir = lambda : "someplace"
218 219 path.get_xdg_dir = lambda : None
219 220 path._writable_dir = lambda path: True
220 221 os.name = "posix"
221 222 env.pop('IPYTHON_DIR', None)
222 223 env.pop('IPYTHONDIR', None)
223 224 env.pop('XDG_CONFIG_HOME', None)
224 225 ipdir = path.get_ipython_dir()
225 226 nt.assert_equal(ipdir, os.path.join("someplace", ".ipython"))
226 227
227 228 @with_environment
228 229 def test_get_ipython_dir_3():
229 230 """test_get_ipython_dir_3, use XDG if defined, and .ipython doesn't exist."""
230 231 path.get_home_dir = lambda : "someplace"
231 232 path._writable_dir = lambda path: True
232 233 os.name = "posix"
233 234 env.pop('IPYTHON_DIR', None)
234 235 env.pop('IPYTHONDIR', None)
235 236 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
236 237 ipdir = path.get_ipython_dir()
237 238 if sys.platform == "darwin":
238 239 expected = os.path.join("someplace", ".ipython")
239 240 else:
240 241 expected = os.path.join(XDG_TEST_DIR, "ipython")
241 242 nt.assert_equal(ipdir, expected)
242 243
243 244 @with_environment
244 245 def test_get_ipython_dir_4():
245 246 """test_get_ipython_dir_4, use XDG if both exist."""
246 247 path.get_home_dir = lambda : HOME_TEST_DIR
247 248 os.name = "posix"
248 249 env.pop('IPYTHON_DIR', None)
249 250 env.pop('IPYTHONDIR', None)
250 251 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
251 252 ipdir = path.get_ipython_dir()
252 253 if sys.platform == "darwin":
253 254 expected = os.path.join(HOME_TEST_DIR, ".ipython")
254 255 else:
255 256 expected = os.path.join(XDG_TEST_DIR, "ipython")
256 257 nt.assert_equal(ipdir, expected)
257 258
258 259 @with_environment
259 260 def test_get_ipython_dir_5():
260 261 """test_get_ipython_dir_5, use .ipython if exists and XDG defined, but doesn't exist."""
261 262 path.get_home_dir = lambda : HOME_TEST_DIR
262 263 os.name = "posix"
263 264 env.pop('IPYTHON_DIR', None)
264 265 env.pop('IPYTHONDIR', None)
265 266 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
266 267 os.rmdir(os.path.join(XDG_TEST_DIR, 'ipython'))
267 268 ipdir = path.get_ipython_dir()
268 269 nt.assert_equal(ipdir, IP_TEST_DIR)
269 270
270 271 @with_environment
271 272 def test_get_ipython_dir_6():
272 273 """test_get_ipython_dir_6, use XDG if defined and neither exist."""
273 274 xdg = os.path.join(HOME_TEST_DIR, 'somexdg')
274 275 os.mkdir(xdg)
275 276 shutil.rmtree(os.path.join(HOME_TEST_DIR, '.ipython'))
276 277 path.get_home_dir = lambda : HOME_TEST_DIR
277 278 path.get_xdg_dir = lambda : xdg
278 279 os.name = "posix"
279 280 env.pop('IPYTHON_DIR', None)
280 281 env.pop('IPYTHONDIR', None)
281 282 env.pop('XDG_CONFIG_HOME', None)
282 283 xdg_ipdir = os.path.join(xdg, "ipython")
283 284 ipdir = path.get_ipython_dir()
284 285 nt.assert_equal(ipdir, xdg_ipdir)
285 286
286 287 @with_environment
287 288 def test_get_ipython_dir_7():
288 289 """test_get_ipython_dir_7, test home directory expansion on IPYTHONDIR"""
289 290 path._writable_dir = lambda path: True
290 291 home_dir = os.path.normpath(os.path.expanduser('~'))
291 292 env['IPYTHONDIR'] = os.path.join('~', 'somewhere')
292 293 ipdir = path.get_ipython_dir()
293 294 nt.assert_equal(ipdir, os.path.join(home_dir, 'somewhere'))
294 295
295 296
296 297 @with_environment
297 298 def test_get_xdg_dir_0():
298 299 """test_get_xdg_dir_0, check xdg_dir"""
299 300 reload(path)
300 301 path._writable_dir = lambda path: True
301 302 path.get_home_dir = lambda : 'somewhere'
302 303 os.name = "posix"
303 304 sys.platform = "linux2"
304 305 env.pop('IPYTHON_DIR', None)
305 306 env.pop('IPYTHONDIR', None)
306 307 env.pop('XDG_CONFIG_HOME', None)
307 308
308 309 nt.assert_equal(path.get_xdg_dir(), os.path.join('somewhere', '.config'))
309 310
310 311
311 312 @with_environment
312 313 def test_get_xdg_dir_1():
313 314 """test_get_xdg_dir_1, check nonexistant xdg_dir"""
314 315 reload(path)
315 316 path.get_home_dir = lambda : HOME_TEST_DIR
316 317 os.name = "posix"
317 318 sys.platform = "linux2"
318 319 env.pop('IPYTHON_DIR', None)
319 320 env.pop('IPYTHONDIR', None)
320 321 env.pop('XDG_CONFIG_HOME', None)
321 322 nt.assert_equal(path.get_xdg_dir(), None)
322 323
323 324 @with_environment
324 325 def test_get_xdg_dir_2():
325 326 """test_get_xdg_dir_2, check xdg_dir default to ~/.config"""
326 327 reload(path)
327 328 path.get_home_dir = lambda : HOME_TEST_DIR
328 329 os.name = "posix"
329 330 sys.platform = "linux2"
330 331 env.pop('IPYTHON_DIR', None)
331 332 env.pop('IPYTHONDIR', None)
332 333 env.pop('XDG_CONFIG_HOME', None)
333 334 cfgdir=os.path.join(path.get_home_dir(), '.config')
334 335 if not os.path.exists(cfgdir):
335 336 os.makedirs(cfgdir)
336 337
337 338 nt.assert_equal(path.get_xdg_dir(), cfgdir)
338 339
339 340 @with_environment
340 341 def test_get_xdg_dir_3():
341 342 """test_get_xdg_dir_3, check xdg_dir not used on OS X"""
342 343 reload(path)
343 344 path.get_home_dir = lambda : HOME_TEST_DIR
344 345 os.name = "posix"
345 346 sys.platform = "darwin"
346 347 env.pop('IPYTHON_DIR', None)
347 348 env.pop('IPYTHONDIR', None)
348 349 env.pop('XDG_CONFIG_HOME', None)
349 350 cfgdir=os.path.join(path.get_home_dir(), '.config')
350 351 if not os.path.exists(cfgdir):
351 352 os.makedirs(cfgdir)
352 353
353 354 nt.assert_equal(path.get_xdg_dir(), None)
354 355
355 356 def test_filefind():
356 357 """Various tests for filefind"""
357 358 f = tempfile.NamedTemporaryFile()
358 359 # print 'fname:',f.name
359 360 alt_dirs = path.get_ipython_dir()
360 361 t = path.filefind(f.name, alt_dirs)
361 362 # print 'found:',t
362 363
363 364
364 365 def test_get_ipython_package_dir():
365 366 ipdir = path.get_ipython_package_dir()
366 367 nt.assert_true(os.path.isdir(ipdir))
367 368
368 369
369 370 def test_get_ipython_module_path():
370 371 ipapp_path = path.get_ipython_module_path('IPython.frontend.terminal.ipapp')
371 372 nt.assert_true(os.path.isfile(ipapp_path))
372 373
373 374
374 375 @dec.skip_if_not_win32
375 376 def test_get_long_path_name_win32():
376 377 p = path.get_long_path_name('c:\\docume~1')
377 378 nt.assert_equal(p,u'c:\\Documents and Settings')
378 379
379 380
380 381 @dec.skip_win32
381 382 def test_get_long_path_name():
382 383 p = path.get_long_path_name('/usr/local')
383 384 nt.assert_equal(p,'/usr/local')
384 385
385 386 @dec.skip_win32 # can't create not-user-writable dir on win
386 387 @with_environment
387 388 def test_not_writable_ipdir():
388 389 tmpdir = tempfile.mkdtemp()
389 390 os.name = "posix"
390 391 env.pop('IPYTHON_DIR', None)
391 392 env.pop('IPYTHONDIR', None)
392 393 env.pop('XDG_CONFIG_HOME', None)
393 394 env['HOME'] = tmpdir
394 395 ipdir = os.path.join(tmpdir, '.ipython')
395 396 os.mkdir(ipdir)
396 397 os.chmod(ipdir, 600)
397 398 with AssertPrints('is not a writable location', channel='stderr'):
398 399 ipdir = path.get_ipython_dir()
399 400 env.pop('IPYTHON_DIR', None)
400 401
401 402 def test_unquote_filename():
402 403 for win32 in (True, False):
403 404 nt.assert_equal(path.unquote_filename('foo.py', win32=win32), 'foo.py')
404 405 nt.assert_equal(path.unquote_filename('foo bar.py', win32=win32), 'foo bar.py')
405 406 nt.assert_equal(path.unquote_filename('"foo.py"', win32=True), 'foo.py')
406 407 nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=True), 'foo bar.py')
407 408 nt.assert_equal(path.unquote_filename("'foo.py'", win32=True), 'foo.py')
408 409 nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=True), 'foo bar.py')
409 410 nt.assert_equal(path.unquote_filename('"foo.py"', win32=False), '"foo.py"')
410 411 nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=False), '"foo bar.py"')
411 412 nt.assert_equal(path.unquote_filename("'foo.py'", win32=False), "'foo.py'")
412 413 nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=False), "'foo bar.py'")
413 414
414 415 @with_environment
415 416 def test_get_py_filename():
416 417 os.chdir(TMP_TEST_DIR)
417 418 for win32 in (True, False):
418 419 with make_tempfile('foo.py'):
419 420 nt.assert_equal(path.get_py_filename('foo.py', force_win32=win32), 'foo.py')
420 421 nt.assert_equal(path.get_py_filename('foo', force_win32=win32), 'foo.py')
421 422 with make_tempfile('foo'):
422 423 nt.assert_equal(path.get_py_filename('foo', force_win32=win32), 'foo')
423 424 nt.assert_raises(IOError, path.get_py_filename, 'foo.py', force_win32=win32)
424 425 nt.assert_raises(IOError, path.get_py_filename, 'foo', force_win32=win32)
425 426 nt.assert_raises(IOError, path.get_py_filename, 'foo.py', force_win32=win32)
426 427 true_fn = 'foo with spaces.py'
427 428 with make_tempfile(true_fn):
428 429 nt.assert_equal(path.get_py_filename('foo with spaces', force_win32=win32), true_fn)
429 430 nt.assert_equal(path.get_py_filename('foo with spaces.py', force_win32=win32), true_fn)
430 431 if win32:
431 432 nt.assert_equal(path.get_py_filename('"foo with spaces.py"', force_win32=True), true_fn)
432 433 nt.assert_equal(path.get_py_filename("'foo with spaces.py'", force_win32=True), true_fn)
433 434 else:
434 435 nt.assert_raises(IOError, path.get_py_filename, '"foo with spaces.py"', force_win32=False)
435 436 nt.assert_raises(IOError, path.get_py_filename, "'foo with spaces.py'", force_win32=False)
436 437
437 438 def test_unicode_in_filename():
438 439 """When a file doesn't exist, the exception raised should be safe to call
439 440 str() on - i.e. in Python 2 it must only have ASCII characters.
440 441
441 442 https://github.com/ipython/ipython/issues/875
442 443 """
443 444 try:
444 445 # these calls should not throw unicode encode exceptions
445 446 path.get_py_filename(u'fooéè.py', force_win32=False)
446 447 except IOError as ex:
447 448 str(ex)
448 449
449 450
450 def test_shellglob():
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
451 class TestShellGlob(object):
455 452
456 with TemporaryDirectory() as td:
457 save = os.getcwdu()
458 try:
459 os.chdir(td)
453 @classmethod
454 def setUpClass(cls):
455 cls.filenames_start_with_a = map('a{0}'.format, range(3))
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 462 # Create empty files
462 for fname in filenames:
463 for fname in cls.filenames:
463 464 open(os.path.join(td, fname), 'w').close()
464 465
465 def assert_match(patterns, matches):
466 # glob returns unordered list. that's why sorted is required.
467 nt.assert_equals(sorted(path.shellglob(patterns)),
468 sorted(matches))
469
470 assert_match(['*'], filenames)
471 assert_match(['a*'], filenames_start_with_a)
472 assert_match(['*c'], ['*c'])
473 assert_match(['*', 'a*', '*b', '*c'],
474 filenames
475 + filenames_start_with_a
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]'])
466 @classmethod
467 def tearDownClass(cls):
468 cls.tempdir.cleanup()
469
470 @classmethod
471 @contextmanager
472 def in_tempdir(cls):
473 save = os.getcwdu()
474 try:
475 os.chdir(cls.tempdir.name)
476 yield
483 477 finally:
484 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 519 def test_unescape_glob():
488 520 nt.assert_equals(path.unescape_glob(r'\*\[\!\]\?'), '*[!]?')
489 521 nt.assert_equals(path.unescape_glob(r'\\*'), r'\*')
490 522 nt.assert_equals(path.unescape_glob(r'\\\*'), r'\*')
491 523 nt.assert_equals(path.unescape_glob(r'\\a'), r'\a')
492 524 nt.assert_equals(path.unescape_glob(r'\a'), r'\a')
@@ -1,36 +1,37 b''
1 1 include README.rst
2 include COPYING.txt
2 3 include ipython.py
3 4 include setupbase.py
4 5 include setupegg.py
5 6
6 7 graft setupext
7 8
8 9 graft scripts
9 10
10 11 # Load main dir but exclude things we don't want in the distro
11 12 graft IPython
12 13 prune IPython/deathrow
13 14 prune IPython/external/js
14 15 prune IPython/frontend/html/notebook/static/mathjax
15 16
16 17 # Include some specific files and data resources we need
17 18 include IPython/.git_commit_info.ini
18 19 include IPython/frontend/qt/console/resources/icon/IPythonConsole.svg
19 20
20 21 # Documentation
21 22 graft docs
22 23 exclude docs/\#*
23 24 exclude docs/man/*.1.gz
24 25
25 26 # docs subdirs we want to skip
26 27 prune docs/attic
27 28 prune docs/build
28 29 prune docs/gh-pages
29 30 prune docs/dist
30 31
31 32 # Patterns to exclude from any directory
32 33 global-exclude *~
33 34 global-exclude *.flc
34 35 global-exclude *.pyc
35 36 global-exclude *.pyo
36 37 global-exclude .dircopy.log
@@ -1,347 +1,259 b''
1 1 {
2 2 "metadata": {
3 3 "name": "Typesetting Math Using MathJax"
4 4 },
5 5 "nbformat": 3,
6 6 "nbformat_minor": 0,
7 7 "worksheets": [
8 8 {
9 9 "cells": [
10 10 {
11 11 "cell_type": "markdown",
12 12 "metadata": {},
13 13 "source": [
14 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 18 "cell_type": "markdown",
19 19 "metadata": {},
20 20 "source": [
21 21 "# Motivating Examples\n",
22 22 "\n",
23 23 "---\n",
24 24 "\n",
25 25 "## The Lorenz Equations\n",
26 26 "### Source\n",
27 27 "```\\begin{aligned}\n",
28 28 "\\dot{x} & = \\sigma(y-x) \\\\\n",
29 29 "\\dot{y} & = \\rho x - y - xz \\\\\n",
30 30 "\\dot{z} & = -\\beta z + xy\n",
31 31 "\\end{aligned}\n",
32 32 "```\n",
33 33 "### Display\n",
34 34 "\\begin{aligned}\n",
35 35 "\\dot{x} & = \\sigma(y-x) \\\\\n",
36 36 "\\dot{y} & = \\rho x - y - xz \\\\\n",
37 37 "\\dot{z} & = -\\beta z + xy\n",
38 38 "\\end{aligned}"
39 39 ]
40 40 },
41 41 {
42 42 "cell_type": "markdown",
43 43 "metadata": {},
44 44 "source": [
45 45 "## The Cauchy-Schwarz Inequality\n",
46 46 "### Source\n",
47 47 "```\\begin{equation*}\n",
48 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 49 "\\end{equation*}\n",
50 50 "```\n",
51 51 "### Display\n",
52 52 "\\begin{equation*}\n",
53 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 54 "\\end{equation*}"
55 55 ]
56 56 },
57 57 {
58 58 "cell_type": "markdown",
59 59 "metadata": {},
60 60 "source": [
61 61 "## A Cross Product Formula\n",
62 62 "### Source\n",
63 63 "```\\begin{equation*}\n",
64 64 "\\mathbf{V}_1 \\times \\mathbf{V}_2 = \\begin{vmatrix}\n",
65 65 "\\mathbf{i} & \\mathbf{j} & \\mathbf{k} \\\\\n",
66 66 "\\frac{\\partial X}{\\partial u} & \\frac{\\partial Y}{\\partial u} & 0 \\\\\n",
67 67 "\\frac{\\partial X}{\\partial v} & \\frac{\\partial Y}{\\partial v} & 0\n",
68 68 "\\end{vmatrix} \n",
69 69 "\\end{equation*}\n",
70 70 "```\n",
71 71 "### Display\n",
72 72 "\\begin{equation*}\n",
73 73 "\\mathbf{V}_1 \\times \\mathbf{V}_2 = \\begin{vmatrix}\n",
74 74 "\\mathbf{i} & \\mathbf{j} & \\mathbf{k} \\\\\n",
75 75 "\\frac{\\partial X}{\\partial u} & \\frac{\\partial Y}{\\partial u} & 0 \\\\\n",
76 76 "\\frac{\\partial X}{\\partial v} & \\frac{\\partial Y}{\\partial v} & 0\n",
77 77 "\\end{vmatrix} \n",
78 78 "\\end{equation*}"
79 79 ]
80 80 },
81 81 {
82 82 "cell_type": "markdown",
83 83 "metadata": {},
84 84 "source": [
85 85 "## The probability of getting \\(k\\) heads when flipping \\(n\\) coins is\n",
86 86 "### Source\n",
87 87 "```\\begin{equation*}\n",
88 88 "P(E) = {n \\choose k} p^k (1-p)^{ n-k} \n",
89 89 "\\end{equation*}\n",
90 90 "```\n",
91 91 "### Display\n",
92 92 "\\begin{equation*}\n",
93 93 "P(E) = {n \\choose k} p^k (1-p)^{ n-k} \n",
94 94 "\\end{equation*}"
95 95 ]
96 96 },
97 97 {
98 98 "cell_type": "markdown",
99 99 "metadata": {},
100 100 "source": [
101 101 "## An Identity of Ramanujan\n",
102 102 "### Source\n",
103 103 "```\\begin{equation*}\n",
104 104 "\\frac{1}{\\Bigl(\\sqrt{\\phi \\sqrt{5}}-\\phi\\Bigr) e^{\\frac25 \\pi}} =\n",
105 105 "1+\\frac{e^{-2\\pi}} {1+\\frac{e^{-4\\pi}} {1+\\frac{e^{-6\\pi}}\n",
106 106 "{1+\\frac{e^{-8\\pi}} {1+\\ldots} } } } \n",
107 107 "\\end{equation*}\n",
108 108 "```\n",
109 109 "### Display\n",
110 110 "\\begin{equation*}\n",
111 111 "\\frac{1}{\\Bigl(\\sqrt{\\phi \\sqrt{5}}-\\phi\\Bigr) e^{\\frac25 \\pi}} =\n",
112 112 "1+\\frac{e^{-2\\pi}} {1+\\frac{e^{-4\\pi}} {1+\\frac{e^{-6\\pi}}\n",
113 113 "{1+\\frac{e^{-8\\pi}} {1+\\ldots} } } } \n",
114 114 "\\end{equation*}"
115 115 ]
116 116 },
117 117 {
118 118 "cell_type": "markdown",
119 119 "metadata": {},
120 120 "source": [
121 121 "## A Rogers-Ramanujan Identity\n",
122 122 "### Source\n",
123 123 "```\\begin{equation*}\n",
124 124 "1 + \\frac{q^2}{(1-q)}+\\frac{q^6}{(1-q)(1-q^2)}+\\cdots =\n",
125 125 "\\prod_{j=0}^{\\infty}\\frac{1}{(1-q^{5j+2})(1-q^{5j+3})},\n",
126 126 "\\quad\\quad \\text{for $|q|<1$}. \n",
127 127 "\\end{equation*}\n",
128 128 "```\n",
129 129 "### Display\n",
130 130 "\\begin{equation*}\n",
131 131 "1 + \\frac{q^2}{(1-q)}+\\frac{q^6}{(1-q)(1-q^2)}+\\cdots =\n",
132 132 "\\prod_{j=0}^{\\infty}\\frac{1}{(1-q^{5j+2})(1-q^{5j+3})},\n",
133 133 "\\quad\\quad \\text{for $|q|<1$}. \n",
134 134 "\\end{equation*}"
135 135 ]
136 136 },
137 137 {
138 138 "cell_type": "markdown",
139 139 "metadata": {},
140 140 "source": [
141 141 "## Maxwell's Equations\n",
142 142 "### Source\n",
143 143 "```\\begin{aligned}\n",
144 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 145 "\\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\\n",
146 146 "\\nabla \\cdot \\vec{\\mathbf{B}} & = 0 \n",
147 147 "\\end{aligned}\n",
148 148 "```\n",
149 149 "### Display\n",
150 150 "\\begin{aligned}\n",
151 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 152 "\\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\\n",
153 153 "\\nabla \\cdot \\vec{\\mathbf{B}} & = 0 \n",
154 154 "\\end{aligned}"
155 155 ]
156 156 },
157 157 {
158 158 "cell_type": "markdown",
159 159 "metadata": {},
160 160 "source": [
161 161 "# Equation Numbering and References\n",
162 162 "\n",
163 163 "---\n",
164 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",
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*}"
165 "Equation numbering and referencing will be available in a future version of IPython."
246 166 ]
247 167 },
248 168 {
249 169 "cell_type": "markdown",
250 170 "metadata": {},
251 171 "source": [
252 172 "# Inline Typesetting (Mixing Markdown and TeX)\n",
253 173 "\n",
254 174 "---\n",
255 175 "\n",
256 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 177 "\n",
258 178 "## Source\n",
259 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 180 "```\n",
261 181 "## Display\n",
262 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 186 "cell_type": "markdown",
267 187 "metadata": {},
268 188 "source": [
269 189 "# Other Syntax\n",
270 190 "\n",
271 191 "---\n",
272 192 "\n",
273 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 194 "\n",
275 195 "### Source\n",
276 196 "```$$\n",
277 197 "\\begin{array}{c}\n",
278 198 "y_1 \\\\\\\n",
279 199 "y_2 \\mathtt{t}_i \\\\\\\n",
280 200 "z_{3,4}\n",
281 201 "\\end{array}\n",
282 202 "$$\n",
283 203 "```\n",
284 204 "\n",
285 205 "```\n",
286 206 "$$\n",
287 207 "\\begin{array}{c}\n",
288 208 "y_1 \\cr\n",
289 209 "y_2 \\mathtt{t}_i \\cr\n",
290 210 "y_{3}\n",
291 211 "\\end{array}\n",
292 212 "$$\n",
293 213 "```\n",
294 214 "\n",
295 215 "```\n",
296 216 "$$\\begin{eqnarray} \n",
297 217 "x' &=& &x \\sin\\phi &+& z \\cos\\phi \\\\\n",
298 218 "z' &=& - &x \\cos\\phi &+& z \\sin\\phi \\\\\n",
299 219 "\\end{eqnarray}$$\n",
300 220 "```\n",
301 221 "\n",
302 222 "```\n",
303 223 "$$\n",
304 224 "x=4\n",
305 225 "$$\n",
306 226 "```\n",
307 227 "\n",
308 228 "### Display\n",
309 229 "$$\n",
310 230 "\\begin{array}{c}\n",
311 231 "y_1 \\\\\\\n",
312 232 "y_2 \\mathtt{t}_i \\\\\\\n",
313 233 "z_{3,4}\n",
314 234 "\\end{array}\n",
315 235 "$$\n",
316 236 "\n",
317 237 "$$\n",
318 238 "\\begin{array}{c}\n",
319 239 "y_1 \\cr\n",
320 240 "y_2 \\mathtt{t}_i \\cr\n",
321 241 "y_{3}\n",
322 242 "\\end{array}\n",
323 243 "$$\n",
324 244 "\n",
325 245 "$$\\begin{eqnarray} \n",
326 246 "x' &=& &x \\sin\\phi &+& z \\cos\\phi \\\\\n",
327 247 "z' &=& - &x \\cos\\phi &+& z \\sin\\phi \\\\\n",
328 248 "\\end{eqnarray}$$\n",
329 249 "\n",
330 250 "$$\n",
331 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 256 "metadata": {}
345 257 }
346 258 ]
347 259 } No newline at end of file
@@ -1,1150 +1,1150 b''
1 1 =================
2 2 IPython reference
3 3 =================
4 4
5 5 .. _command_line_options:
6 6
7 7 Command-line usage
8 8 ==================
9 9
10 10 You start IPython with the command::
11 11
12 12 $ ipython [options] files
13 13
14 14 .. note::
15 15
16 16 For IPython on Python 3, use ``ipython3`` in place of ``ipython``.
17 17
18 18 If invoked with no options, it executes all the files listed in sequence
19 19 and drops you into the interpreter while still acknowledging any options
20 20 you may have set in your ipython_config.py. This behavior is different from
21 21 standard Python, which when called as python -i will only execute one
22 22 file and ignore your configuration setup.
23 23
24 24 Please note that some of the configuration options are not available at
25 25 the command line, simply because they are not practical here. Look into
26 26 your configuration files for details on those. There are separate configuration
27 27 files for each profile, and the files look like "ipython_config.py" or
28 28 "ipython_config_<frontendname>.py". Profile directories look like
29 29 "profile_profilename" and are typically installed in the IPYTHONDIR directory.
30 30 For Linux users, this will be $HOME/.config/ipython, and for other users it
31 31 will be $HOME/.ipython. For Windows users, $HOME resolves to C:\\Documents and
32 32 Settings\\YourUserName in most instances.
33 33
34 34
35 35 Eventloop integration
36 36 ---------------------
37 37
38 38 Previously IPython had command line options for controlling GUI event loop
39 39 integration (-gthread, -qthread, -q4thread, -wthread, -pylab). As of IPython
40 40 version 0.11, these have been removed. Please see the new ``%gui``
41 41 magic command or :ref:`this section <gui_support>` for details on the new
42 42 interface, or specify the gui at the commandline::
43 43
44 44 $ ipython --gui=qt
45 45
46 46
47 47 Command-line Options
48 48 --------------------
49 49
50 50 To see the options IPython accepts, use ``ipython --help`` (and you probably
51 51 should run the output through a pager such as ``ipython --help | less`` for
52 52 more convenient reading). This shows all the options that have a single-word
53 53 alias to control them, but IPython lets you configure all of its objects from
54 54 the command-line by passing the full class name and a corresponding value; type
55 55 ``ipython --help-all`` to see this full list. For example::
56 56
57 57 ipython --pylab qt
58 58
59 59 is equivalent to::
60 60
61 61 ipython --TerminalIPythonApp.pylab='qt'
62 62
63 63 Note that in the second form, you *must* use the equal sign, as the expression
64 64 is evaluated as an actual Python assignment. While in the above example the
65 65 short form is more convenient, only the most common options have a short form,
66 66 while any configurable variable in IPython can be set at the command-line by
67 67 using the long form. This long form is the same syntax used in the
68 68 configuration files, if you want to set these options permanently.
69 69
70 70
71 71 Interactive use
72 72 ===============
73 73
74 74 IPython is meant to work as a drop-in replacement for the standard interactive
75 75 interpreter. As such, any code which is valid python should execute normally
76 76 under IPython (cases where this is not true should be reported as bugs). It
77 77 does, however, offer many features which are not available at a standard python
78 78 prompt. What follows is a list of these.
79 79
80 80
81 81 Caution for Windows users
82 82 -------------------------
83 83
84 84 Windows, unfortunately, uses the '\\' character as a path separator. This is a
85 85 terrible choice, because '\\' also represents the escape character in most
86 86 modern programming languages, including Python. For this reason, using '/'
87 87 character is recommended if you have problems with ``\``. However, in Windows
88 88 commands '/' flags options, so you can not use it for the root directory. This
89 89 means that paths beginning at the root must be typed in a contrived manner
90 90 like: ``%copy \opt/foo/bar.txt \tmp``
91 91
92 92 .. _magic:
93 93
94 94 Magic command system
95 95 --------------------
96 96
97 97 IPython will treat any line whose first character is a % as a special
98 98 call to a 'magic' function. These allow you to control the behavior of
99 99 IPython itself, plus a lot of system-type features. They are all
100 100 prefixed with a % character, but parameters are given without
101 101 parentheses or quotes.
102 102
103 103 Lines that begin with ``%%`` signal a *cell magic*: they take as arguments not
104 104 only the rest of the current line, but all lines below them as well, in the
105 105 current execution block. Cell magics can in fact make arbitrary modifications
106 106 to the input they receive, which need not even be valid Python code at all.
107 107 They receive the whole block as a single string.
108 108
109 109 As a line magic example, the ``%cd`` magic works just like the OS command of
110 110 the same name::
111 111
112 112 In [8]: %cd
113 113 /home/fperez
114 114
115 115 The following uses the builtin ``timeit`` in cell mode::
116 116
117 117 In [10]: %%timeit x = range(10000)
118 118 ...: min(x)
119 119 ...: max(x)
120 120 ...:
121 121 1000 loops, best of 3: 438 us per loop
122 122
123 123 In this case, ``x = range(10000)`` is called as the line argument, and the
124 124 block with ``min(x)`` and ``max(x)`` is called as the cell body. The
125 125 ``timeit`` magic receives both.
126 126
127 127 If you have 'automagic' enabled (as it by default), you don't need to type in
128 128 the single ``%`` explicitly for line magics; IPython will scan its internal
129 129 list of magic functions and call one if it exists. With automagic on you can
130 130 then just type ``cd mydir`` to go to directory 'mydir'::
131 131
132 132 In [9]: cd mydir
133 133 /home/fperez/mydir
134 134
135 135 Note that cell magics *always* require an explicit ``%%`` prefix, automagic
136 136 calling only works for line magics.
137 137
138 138 The automagic system has the lowest possible precedence in name searches, so
139 139 defining an identifier with the same name as an existing magic function will
140 140 shadow it for automagic use. You can still access the shadowed magic function
141 141 by explicitly using the ``%`` character at the beginning of the line.
142 142
143 143 An example (with automagic on) should clarify all this:
144 144
145 145 .. sourcecode:: ipython
146 146
147 147 In [1]: cd ipython # %cd is called by automagic
148 148 /home/fperez/ipython
149 149
150 150 In [2]: cd=1 # now cd is just a variable
151 151
152 152 In [3]: cd .. # and doesn't work as a function anymore
153 153 File "<ipython-input-3-9fedb3aff56c>", line 1
154 154 cd ..
155 155 ^
156 156 SyntaxError: invalid syntax
157 157
158 158
159 159 In [4]: %cd .. # but %cd always works
160 160 /home/fperez
161 161
162 162 In [5]: del cd # if you remove the cd variable, automagic works again
163 163
164 164 In [6]: cd ipython
165 165
166 166 /home/fperez/ipython
167 167
168 168 Defining your own magics
169 169 ~~~~~~~~~~~~~~~~~~~~~~~~
170 170
171 171 There are two main ways to define your own magic functions: from standalone
172 172 functions and by inheriting from a base class provided by IPython:
173 173 :class:`IPython.core.magic.Magics`. Below we show code you can place in a file
174 174 that you load from your configuration, such as any file in the ``startup``
175 175 subdirectory of your default IPython profile.
176 176
177 177 First, let us see the simplest case. The following shows how to create a line
178 178 magic, a cell one and one that works in both modes, using just plain functions:
179 179
180 180 .. sourcecode:: python
181 181
182 182 from IPython.core.magic import (register_line_magic, register_cell_magic,
183 183 register_line_cell_magic)
184 184
185 185 @register_line_magic
186 186 def lmagic(line):
187 187 "my line magic"
188 188 return line
189 189
190 190 @register_cell_magic
191 191 def cmagic(line, cell):
192 192 "my cell magic"
193 193 return line, cell
194 194
195 195 @register_line_cell_magic
196 196 def lcmagic(line, cell=None):
197 197 "Magic that works both as %lcmagic and as %%lcmagic"
198 198 if cell is None:
199 199 print "Called as line magic"
200 200 return line
201 201 else:
202 202 print "Called as cell magic"
203 203 return line, cell
204 204
205 205 # We delete these to avoid name conflicts for automagic to work
206 206 del lmagic, lcmagic
207 207
208 208
209 209 You can also create magics of all three kinds by inheriting from the
210 210 :class:`IPython.core.magic.Magics` class. This lets you create magics that can
211 211 potentially hold state in between calls, and that have full access to the main
212 212 IPython object:
213 213
214 214 .. sourcecode:: python
215 215
216 216 # This code can be put in any Python module, it does not require IPython
217 217 # itself to be running already. It only creates the magics subclass but
218 218 # doesn't instantiate it yet.
219 219 from IPython.core.magic import (Magics, magics_class, line_magic,
220 220 cell_magic, line_cell_magic)
221 221
222 222 # The class MUST call this class decorator at creation time
223 223 @magics_class
224 224 class MyMagics(Magics):
225 225
226 226 @line_magic
227 227 def lmagic(self, line):
228 228 "my line magic"
229 229 print "Full access to the main IPython object:", self.shell
230 230 print "Variables in the user namespace:", self.user_ns.keys()
231 231 return line
232 232
233 233 @cell_magic
234 234 def cmagic(self, line, cell):
235 235 "my cell magic"
236 236 return line, cell
237 237
238 238 @line_cell_magic
239 239 def lcmagic(self, line, cell=None):
240 240 "Magic that works both as %lcmagic and as %%lcmagic"
241 241 if cell is None:
242 242 print "Called as line magic"
243 243 return line
244 244 else:
245 245 print "Called as cell magic"
246 246 return line, cell
247 247
248 248
249 249 # In order to actually use these magics, you must register them with a
250 250 # running IPython. This code must be placed in a file that is loaded once
251 251 # IPython is up and running:
252 252 ip = get_ipython()
253 253 # You can register the class itself without instantiating it. IPython will
254 254 # call the default constructor on it.
255 255 ip.register_magics(MyMagics)
256 256
257 257 If you want to create a class with a different constructor that holds
258 258 additional state, then you should always call the parent constructor and
259 259 instantiate the class yourself before registration:
260 260
261 261 .. sourcecode:: python
262 262
263 263 @magics_class
264 264 class StatefulMagics(Magics):
265 265 "Magics that hold additional state"
266 266
267 267 def __init__(self, shell, data):
268 268 # You must call the parent constructor
269 269 super(StatefulMagics, self).__init__(shell)
270 270 self.data = data
271 271
272 272 # etc...
273 273
274 274 # This class must then be registered with a manually created instance,
275 275 # since its constructor has different arguments from the default:
276 276 ip = get_ipython()
277 277 magics = StatefulMagics(ip, some_data)
278 278 ip.register_magics(magics)
279 279
280 280
281 281 In earlier versions, IPython had an API for the creation of line magics (cell
282 282 magics did not exist at the time) that required you to create functions with a
283 283 method-looking signature and to manually pass both the function and the name.
284 284 While this API is no longer recommended, it remains indefinitely supported for
285 285 backwards compatibility purposes. With the old API, you'd create a magic as
286 286 follows:
287 287
288 288 .. sourcecode:: python
289 289
290 290 def func(self, line):
291 291 print "Line magic called with line:", line
292 292 print "IPython object:", self.shell
293 293
294 294 ip = get_ipython()
295 295 # Declare this function as the magic %mycommand
296 296 ip.define_magic('mycommand', func)
297 297
298 298 Type ``%magic`` for more information, including a list of all available magic
299 299 functions at any time and their docstrings. You can also type
300 300 ``%magic_function_name?`` (see :ref:`below <dynamic_object_info>` for
301 301 information on the '?' system) to get information about any particular magic
302 302 function you are interested in.
303 303
304 304 The API documentation for the :mod:`IPython.core.magic` module contains the full
305 305 docstrings of all currently available magic commands.
306 306
307 307
308 308 Access to the standard Python help
309 309 ----------------------------------
310 310
311 311 Simply type ``help()`` to access Python's standard help system. You can
312 312 also type ``help(object)`` for information about a given object, or
313 313 ``help('keyword')`` for information on a keyword. You may need to configure your
314 314 PYTHONDOCS environment variable for this feature to work correctly.
315 315
316 316 .. _dynamic_object_info:
317 317
318 318 Dynamic object information
319 319 --------------------------
320 320
321 321 Typing ``?word`` or ``word?`` prints detailed information about an object. If
322 322 certain strings in the object are too long (e.g. function signatures) they get
323 323 snipped in the center for brevity. This system gives access variable types and
324 324 values, docstrings, function prototypes and other useful information.
325 325
326 326 If the information will not fit in the terminal, it is displayed in a pager
327 327 (``less`` if available, otherwise a basic internal pager).
328 328
329 329 Typing ``??word`` or ``word??`` gives access to the full information, including
330 330 the source code where possible. Long strings are not snipped.
331 331
332 332 The following magic functions are particularly useful for gathering
333 333 information about your working environment. You can get more details by
334 334 typing ``%magic`` or querying them individually (``%function_name?``);
335 335 this is just a summary:
336 336
337 337 * **%pdoc <object>**: Print (or run through a pager if too long) the
338 338 docstring for an object. If the given object is a class, it will
339 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 341 object. If the object is a class, print the constructor information.
342 342 * **%psource <object>**: Print (or run through a pager if too long)
343 343 the source code for an object.
344 344 * **%pfile <object>**: Show the entire source file where an object was
345 345 defined via a pager, opening it at the line where the object
346 346 definition begins.
347 347 * **%who/%whos**: These functions give information about identifiers
348 348 you have defined interactively (not things you loaded or defined
349 349 in your configuration files). %who just prints a list of
350 350 identifiers and %whos prints a table with some basic details about
351 351 each identifier.
352 352
353 353 Note that the dynamic object information functions (?/??, ``%pdoc``,
354 354 ``%pfile``, ``%pdef``, ``%psource``) work on object attributes, as well as
355 355 directly on variables. For example, after doing ``import os``, you can use
356 356 ``os.path.abspath??``.
357 357
358 358 .. _readline:
359 359
360 360 Readline-based features
361 361 -----------------------
362 362
363 363 These features require the GNU readline library, so they won't work if your
364 364 Python installation lacks readline support. We will first describe the default
365 365 behavior IPython uses, and then how to change it to suit your preferences.
366 366
367 367
368 368 Command line completion
369 369 +++++++++++++++++++++++
370 370
371 371 At any time, hitting TAB will complete any available python commands or
372 372 variable names, and show you a list of the possible completions if
373 373 there's no unambiguous one. It will also complete filenames in the
374 374 current directory if no python names match what you've typed so far.
375 375
376 376
377 377 Search command history
378 378 ++++++++++++++++++++++
379 379
380 380 IPython provides two ways for searching through previous input and thus
381 381 reduce the need for repetitive typing:
382 382
383 383 1. Start typing, and then use Ctrl-p (previous,up) and Ctrl-n
384 384 (next,down) to search through only the history items that match
385 385 what you've typed so far. If you use Ctrl-p/Ctrl-n at a blank
386 386 prompt, they just behave like normal arrow keys.
387 387 2. Hit Ctrl-r: opens a search prompt. Begin typing and the system
388 388 searches your history for lines that contain what you've typed so
389 389 far, completing as much as it can.
390 390
391 391
392 392 Persistent command history across sessions
393 393 ++++++++++++++++++++++++++++++++++++++++++
394 394
395 395 IPython will save your input history when it leaves and reload it next
396 396 time you restart it. By default, the history file is named
397 397 $IPYTHONDIR/profile_<name>/history.sqlite. This allows you to keep
398 398 separate histories related to various tasks: commands related to
399 399 numerical work will not be clobbered by a system shell history, for
400 400 example.
401 401
402 402
403 403 Autoindent
404 404 ++++++++++
405 405
406 406 IPython can recognize lines ending in ':' and indent the next line,
407 407 while also un-indenting automatically after 'raise' or 'return'.
408 408
409 409 This feature uses the readline library, so it will honor your
410 410 :file:`~/.inputrc` configuration (or whatever file your INPUTRC variable points
411 411 to). Adding the following lines to your :file:`.inputrc` file can make
412 412 indenting/unindenting more convenient (M-i indents, M-u unindents)::
413 413
414 414 $if Python
415 415 "\M-i": " "
416 416 "\M-u": "\d\d\d\d"
417 417 $endif
418 418
419 419 Note that there are 4 spaces between the quote marks after "M-i" above.
420 420
421 421 .. warning::
422 422
423 423 Setting the above indents will cause problems with unicode text entry in
424 424 the terminal.
425 425
426 426 .. warning::
427 427
428 428 Autoindent is ON by default, but it can cause problems with the pasting of
429 429 multi-line indented code (the pasted code gets re-indented on each line). A
430 430 magic function %autoindent allows you to toggle it on/off at runtime. You
431 431 can also disable it permanently on in your :file:`ipython_config.py` file
432 432 (set TerminalInteractiveShell.autoindent=False).
433 433
434 434 If you want to paste multiple lines in the terminal, it is recommended that
435 435 you use ``%paste``.
436 436
437 437
438 438 Customizing readline behavior
439 439 +++++++++++++++++++++++++++++
440 440
441 441 All these features are based on the GNU readline library, which has an
442 442 extremely customizable interface. Normally, readline is configured via a
443 443 file which defines the behavior of the library; the details of the
444 444 syntax for this can be found in the readline documentation available
445 445 with your system or on the Internet. IPython doesn't read this file (if
446 446 it exists) directly, but it does support passing to readline valid
447 447 options via a simple interface. In brief, you can customize readline by
448 448 setting the following options in your configuration file (note
449 449 that these options can not be specified at the command line):
450 450
451 451 * **readline_parse_and_bind**: this holds a list of strings to be executed
452 452 via a readline.parse_and_bind() command. The syntax for valid commands
453 453 of this kind can be found by reading the documentation for the GNU
454 454 readline library, as these commands are of the kind which readline
455 455 accepts in its configuration file.
456 456 * **readline_remove_delims**: a string of characters to be removed
457 457 from the default word-delimiters list used by readline, so that
458 458 completions may be performed on strings which contain them. Do not
459 459 change the default value unless you know what you're doing.
460 460
461 461 You will find the default values in your configuration file.
462 462
463 463
464 464 Session logging and restoring
465 465 -----------------------------
466 466
467 467 You can log all input from a session either by starting IPython with the
468 468 command line switch ``--logfile=foo.py`` (see :ref:`here <command_line_options>`)
469 469 or by activating the logging at any moment with the magic function %logstart.
470 470
471 471 Log files can later be reloaded by running them as scripts and IPython
472 472 will attempt to 'replay' the log by executing all the lines in it, thus
473 473 restoring the state of a previous session. This feature is not quite
474 474 perfect, but can still be useful in many cases.
475 475
476 476 The log files can also be used as a way to have a permanent record of
477 477 any code you wrote while experimenting. Log files are regular text files
478 478 which you can later open in your favorite text editor to extract code or
479 479 to 'clean them up' before using them to replay a session.
480 480
481 481 The `%logstart` function for activating logging in mid-session is used as
482 482 follows::
483 483
484 484 %logstart [log_name [log_mode]]
485 485
486 486 If no name is given, it defaults to a file named 'ipython_log.py' in your
487 487 current working directory, in 'rotate' mode (see below).
488 488
489 489 '%logstart name' saves to file 'name' in 'backup' mode. It saves your
490 490 history up to that point and then continues logging.
491 491
492 492 %logstart takes a second optional parameter: logging mode. This can be
493 493 one of (note that the modes are given unquoted):
494 494
495 495 * [over:] overwrite existing log_name.
496 496 * [backup:] rename (if exists) to log_name~ and start log_name.
497 497 * [append:] well, that says it.
498 498 * [rotate:] create rotating logs log_name.1~, log_name.2~, etc.
499 499
500 500 The %logoff and %logon functions allow you to temporarily stop and
501 501 resume logging to a file which had previously been started with
502 502 %logstart. They will fail (with an explanation) if you try to use them
503 503 before logging has been started.
504 504
505 505 .. _system_shell_access:
506 506
507 507 System shell access
508 508 -------------------
509 509
510 510 Any input line beginning with a ! character is passed verbatim (minus
511 511 the !, of course) to the underlying operating system. For example,
512 512 typing ``!ls`` will run 'ls' in the current directory.
513 513
514 514 Manual capture of command output
515 515 --------------------------------
516 516
517 517 You can assign the result of a system command to a Python variable with the
518 518 syntax ``myfiles = !ls``. This gets machine readable output from stdout
519 519 (e.g. without colours), and splits on newlines. To explicitly get this sort of
520 520 output without assigning to a variable, use two exclamation marks (``!!ls``) or
521 521 the ``%sx`` magic command.
522 522
523 523 The captured list has some convenience features. ``myfiles.n`` or ``myfiles.s``
524 524 returns a string delimited by newlines or spaces, respectively. ``myfiles.p``
525 525 produces `path objects <http://pypi.python.org/pypi/path.py>`_ from the list items.
526 526 See :ref:`string_lists` for details.
527 527
528 528 IPython also allows you to expand the value of python variables when
529 529 making system calls. Wrap variables or expressions in {braces}::
530 530
531 531 In [1]: pyvar = 'Hello world'
532 532 In [2]: !echo "A python variable: {pyvar}"
533 533 A python variable: Hello world
534 534 In [3]: import math
535 535 In [4]: x = 8
536 536 In [5]: !echo {math.factorial(x)}
537 537 40320
538 538
539 539 For simple cases, you can alternatively prepend $ to a variable name::
540 540
541 541 In [6]: !echo $sys.argv
542 542 [/home/fperez/usr/bin/ipython]
543 543 In [7]: !echo "A system variable: $$HOME" # Use $$ for literal $
544 544 A system variable: /home/fperez
545 545
546 546 System command aliases
547 547 ----------------------
548 548
549 549 The %alias magic function allows you to define magic functions which are in fact
550 550 system shell commands. These aliases can have parameters.
551 551
552 552 ``%alias alias_name cmd`` defines 'alias_name' as an alias for 'cmd'
553 553
554 554 Then, typing ``alias_name params`` will execute the system command 'cmd
555 555 params' (from your underlying operating system).
556 556
557 557 You can also define aliases with parameters using %s specifiers (one per
558 558 parameter). The following example defines the parts function as an
559 559 alias to the command 'echo first %s second %s' where each %s will be
560 560 replaced by a positional parameter to the call to %parts::
561 561
562 562 In [1]: %alias parts echo first %s second %s
563 563 In [2]: parts A B
564 564 first A second B
565 565 In [3]: parts A
566 566 ERROR: Alias <parts> requires 2 arguments, 1 given.
567 567
568 568 If called with no parameters, %alias prints the table of currently
569 569 defined aliases.
570 570
571 571 The %rehashx magic allows you to load your entire $PATH as
572 572 ipython aliases. See its docstring for further details.
573 573
574 574
575 575 .. _dreload:
576 576
577 577 Recursive reload
578 578 ----------------
579 579
580 580 The :mod:`IPython.lib.deepreload` module allows you to recursively reload a
581 581 module: changes made to any of its dependencies will be reloaded without
582 582 having to exit. To start using it, do::
583 583
584 584 from IPython.lib.deepreload import reload as dreload
585 585
586 586
587 587 Verbose and colored exception traceback printouts
588 588 -------------------------------------------------
589 589
590 590 IPython provides the option to see very detailed exception tracebacks,
591 591 which can be especially useful when debugging large programs. You can
592 592 run any Python file with the %run function to benefit from these
593 593 detailed tracebacks. Furthermore, both normal and verbose tracebacks can
594 594 be colored (if your terminal supports it) which makes them much easier
595 595 to parse visually.
596 596
597 597 See the magic xmode and colors functions for details (just type %magic).
598 598
599 599 These features are basically a terminal version of Ka-Ping Yee's cgitb
600 600 module, now part of the standard Python library.
601 601
602 602
603 603 .. _input_caching:
604 604
605 605 Input caching system
606 606 --------------------
607 607
608 608 IPython offers numbered prompts (In/Out) with input and output caching
609 609 (also referred to as 'input history'). All input is saved and can be
610 610 retrieved as variables (besides the usual arrow key recall), in
611 611 addition to the %rep magic command that brings a history entry
612 612 up for editing on the next command line.
613 613
614 614 The following GLOBAL variables always exist (so don't overwrite them!):
615 615
616 616 * _i, _ii, _iii: store previous, next previous and next-next previous inputs.
617 617 * In, _ih : a list of all inputs; _ih[n] is the input from line n. If you
618 618 overwrite In with a variable of your own, you can remake the assignment to the
619 619 internal list with a simple ``In=_ih``.
620 620
621 621 Additionally, global variables named _i<n> are dynamically created (<n>
622 622 being the prompt counter), so ``_i<n> == _ih[<n>] == In[<n>]``.
623 623
624 624 For example, what you typed at prompt 14 is available as _i14, _ih[14]
625 625 and In[14].
626 626
627 627 This allows you to easily cut and paste multi line interactive prompts
628 628 by printing them out: they print like a clean string, without prompt
629 629 characters. You can also manipulate them like regular variables (they
630 630 are strings), modify or exec them (typing ``exec _i9`` will re-execute the
631 631 contents of input prompt 9.
632 632
633 633 You can also re-execute multiple lines of input easily by using the
634 634 magic %rerun or %macro functions. The macro system also allows you to re-execute
635 635 previous lines which include magic function calls (which require special
636 636 processing). Type %macro? for more details on the macro system.
637 637
638 638 A history function %hist allows you to see any part of your input
639 639 history by printing a range of the _i variables.
640 640
641 641 You can also search ('grep') through your history by typing
642 642 ``%hist -g somestring``. This is handy for searching for URLs, IP addresses,
643 643 etc. You can bring history entries listed by '%hist -g' up for editing
644 644 with the %recall command, or run them immediately with %rerun.
645 645
646 646 .. _output_caching:
647 647
648 648 Output caching system
649 649 ---------------------
650 650
651 651 For output that is returned from actions, a system similar to the input
652 652 cache exists but using _ instead of _i. Only actions that produce a
653 653 result (NOT assignments, for example) are cached. If you are familiar
654 654 with Mathematica, IPython's _ variables behave exactly like
655 655 Mathematica's % variables.
656 656
657 657 The following GLOBAL variables always exist (so don't overwrite them!):
658 658
659 659 * [_] (a single underscore) : stores previous output, like Python's
660 660 default interpreter.
661 661 * [__] (two underscores): next previous.
662 662 * [___] (three underscores): next-next previous.
663 663
664 664 Additionally, global variables named _<n> are dynamically created (<n>
665 665 being the prompt counter), such that the result of output <n> is always
666 666 available as _<n> (don't use the angle brackets, just the number, e.g.
667 667 _21).
668 668
669 669 These variables are also stored in a global dictionary (not a
670 670 list, since it only has entries for lines which returned a result)
671 671 available under the names _oh and Out (similar to _ih and In). So the
672 672 output from line 12 can be obtained as _12, Out[12] or _oh[12]. If you
673 673 accidentally overwrite the Out variable you can recover it by typing
674 674 'Out=_oh' at the prompt.
675 675
676 676 This system obviously can potentially put heavy memory demands on your
677 677 system, since it prevents Python's garbage collector from removing any
678 678 previously computed results. You can control how many results are kept
679 679 in memory with the option (at the command line or in your configuration
680 680 file) cache_size. If you set it to 0, the whole system is completely
681 681 disabled and the prompts revert to the classic '>>>' of normal Python.
682 682
683 683
684 684 Directory history
685 685 -----------------
686 686
687 687 Your history of visited directories is kept in the global list _dh, and
688 688 the magic %cd command can be used to go to any entry in that list. The
689 689 %dhist command allows you to view this history. Do ``cd -<TAB>`` to
690 690 conveniently view the directory history.
691 691
692 692
693 693 Automatic parentheses and quotes
694 694 --------------------------------
695 695
696 696 These features were adapted from Nathan Gray's LazyPython. They are
697 697 meant to allow less typing for common situations.
698 698
699 699
700 700 Automatic parentheses
701 701 +++++++++++++++++++++
702 702
703 703 Callable objects (i.e. functions, methods, etc) can be invoked like this
704 704 (notice the commas between the arguments)::
705 705
706 706 In [1]: callable_ob arg1, arg2, arg3
707 707 ------> callable_ob(arg1, arg2, arg3)
708 708
709 709 You can force automatic parentheses by using '/' as the first character
710 710 of a line. For example::
711 711
712 712 In [2]: /globals # becomes 'globals()'
713 713
714 714 Note that the '/' MUST be the first character on the line! This won't work::
715 715
716 716 In [3]: print /globals # syntax error
717 717
718 718 In most cases the automatic algorithm should work, so you should rarely
719 719 need to explicitly invoke /. One notable exception is if you are trying
720 720 to call a function with a list of tuples as arguments (the parenthesis
721 721 will confuse IPython)::
722 722
723 723 In [4]: zip (1,2,3),(4,5,6) # won't work
724 724
725 725 but this will work::
726 726
727 727 In [5]: /zip (1,2,3),(4,5,6)
728 728 ------> zip ((1,2,3),(4,5,6))
729 729 Out[5]: [(1, 4), (2, 5), (3, 6)]
730 730
731 731 IPython tells you that it has altered your command line by displaying
732 732 the new command line preceded by ->. e.g.::
733 733
734 734 In [6]: callable list
735 735 ------> callable(list)
736 736
737 737
738 738 Automatic quoting
739 739 +++++++++++++++++
740 740
741 741 You can force automatic quoting of a function's arguments by using ','
742 742 or ';' as the first character of a line. For example::
743 743
744 744 In [1]: ,my_function /home/me # becomes my_function("/home/me")
745 745
746 746 If you use ';' the whole argument is quoted as a single string, while ',' splits
747 747 on whitespace::
748 748
749 749 In [2]: ,my_function a b c # becomes my_function("a","b","c")
750 750
751 751 In [3]: ;my_function a b c # becomes my_function("a b c")
752 752
753 753 Note that the ',' or ';' MUST be the first character on the line! This
754 754 won't work::
755 755
756 756 In [4]: x = ,my_function /home/me # syntax error
757 757
758 758 IPython as your default Python environment
759 759 ==========================================
760 760
761 761 Python honors the environment variable PYTHONSTARTUP and will execute at
762 762 startup the file referenced by this variable. If you put the following code at
763 763 the end of that file, then IPython will be your working environment anytime you
764 764 start Python::
765 765
766 766 from IPython.frontend.terminal.ipapp import launch_new_instance
767 767 launch_new_instance()
768 768 raise SystemExit
769 769
770 770 The ``raise SystemExit`` is needed to exit Python when
771 771 it finishes, otherwise you'll be back at the normal Python '>>>'
772 772 prompt.
773 773
774 774 This is probably useful to developers who manage multiple Python
775 775 versions and don't want to have correspondingly multiple IPython
776 776 versions. Note that in this mode, there is no way to pass IPython any
777 777 command-line options, as those are trapped first by Python itself.
778 778
779 779 .. _Embedding:
780 780
781 781 Embedding IPython
782 782 =================
783 783
784 784 It is possible to start an IPython instance inside your own Python
785 785 programs. This allows you to evaluate dynamically the state of your
786 786 code, operate with your variables, analyze them, etc. Note however that
787 787 any changes you make to values while in the shell do not propagate back
788 788 to the running code, so it is safe to modify your values because you
789 789 won't break your code in bizarre ways by doing so.
790 790
791 791 .. note::
792 792
793 793 At present, trying to embed IPython from inside IPython causes problems. Run
794 794 the code samples below outside IPython.
795 795
796 796 This feature allows you to easily have a fully functional python
797 797 environment for doing object introspection anywhere in your code with a
798 798 simple function call. In some cases a simple print statement is enough,
799 799 but if you need to do more detailed analysis of a code fragment this
800 800 feature can be very valuable.
801 801
802 802 It can also be useful in scientific computing situations where it is
803 803 common to need to do some automatic, computationally intensive part and
804 804 then stop to look at data, plots, etc.
805 805 Opening an IPython instance will give you full access to your data and
806 806 functions, and you can resume program execution once you are done with
807 807 the interactive part (perhaps to stop again later, as many times as
808 808 needed).
809 809
810 810 The following code snippet is the bare minimum you need to include in
811 811 your Python programs for this to work (detailed examples follow later)::
812 812
813 813 from IPython import embed
814 814
815 815 embed() # this call anywhere in your program will start IPython
816 816
817 817 .. note::
818 818
819 819 As of 0.13, you can embed an IPython *kernel*, for use with qtconsole,
820 820 etc. via ``IPython.embed_kernel()`` instead of ``IPython.embed()``.
821 821 It should function just the same as regular embed, but you connect
822 822 an external frontend rather than IPython starting up in the local
823 823 terminal.
824 824
825 825 You can run embedded instances even in code which is itself being run at
826 826 the IPython interactive prompt with '%run <filename>'. Since it's easy
827 827 to get lost as to where you are (in your top-level IPython or in your
828 828 embedded one), it's a good idea in such cases to set the in/out prompts
829 829 to something different for the embedded instances. The code examples
830 830 below illustrate this.
831 831
832 832 You can also have multiple IPython instances in your program and open
833 833 them separately, for example with different options for data
834 834 presentation. If you close and open the same instance multiple times,
835 835 its prompt counters simply continue from each execution to the next.
836 836
837 837 Please look at the docstrings in the :mod:`~IPython.frontend.terminal.embed`
838 838 module for more details on the use of this system.
839 839
840 840 The following sample file illustrating how to use the embedding
841 841 functionality is provided in the examples directory as example-embed.py.
842 842 It should be fairly self-explanatory:
843 843
844 844 .. literalinclude:: ../../examples/core/example-embed.py
845 845 :language: python
846 846
847 847 Once you understand how the system functions, you can use the following
848 848 code fragments in your programs which are ready for cut and paste:
849 849
850 850 .. literalinclude:: ../../examples/core/example-embed-short.py
851 851 :language: python
852 852
853 853 Using the Python debugger (pdb)
854 854 ===============================
855 855
856 856 Running entire programs via pdb
857 857 -------------------------------
858 858
859 859 pdb, the Python debugger, is a powerful interactive debugger which
860 860 allows you to step through code, set breakpoints, watch variables,
861 861 etc. IPython makes it very easy to start any script under the control
862 862 of pdb, regardless of whether you have wrapped it into a 'main()'
863 863 function or not. For this, simply type '%run -d myscript' at an
864 864 IPython prompt. See the %run command's documentation (via '%run?' or
865 865 in Sec. magic_ for more details, including how to control where pdb
866 866 will stop execution first.
867 867
868 868 For more information on the use of the pdb debugger, read the included
869 869 pdb.doc file (part of the standard Python distribution). On a stock
870 870 Linux system it is located at /usr/lib/python2.3/pdb.doc, but the
871 871 easiest way to read it is by using the help() function of the pdb module
872 872 as follows (in an IPython prompt)::
873 873
874 874 In [1]: import pdb
875 875 In [2]: pdb.help()
876 876
877 877 This will load the pdb.doc document in a file viewer for you automatically.
878 878
879 879
880 880 Automatic invocation of pdb on exceptions
881 881 -----------------------------------------
882 882
883 883 IPython, if started with the ``--pdb`` option (or if the option is set in
884 884 your config file) can call the Python pdb debugger every time your code
885 885 triggers an uncaught exception. This feature
886 886 can also be toggled at any time with the %pdb magic command. This can be
887 887 extremely useful in order to find the origin of subtle bugs, because pdb
888 888 opens up at the point in your code which triggered the exception, and
889 889 while your program is at this point 'dead', all the data is still
890 890 available and you can walk up and down the stack frame and understand
891 891 the origin of the problem.
892 892
893 893 Furthermore, you can use these debugging facilities both with the
894 894 embedded IPython mode and without IPython at all. For an embedded shell
895 895 (see sec. Embedding_), simply call the constructor with
896 896 ``--pdb`` in the argument string and pdb will automatically be called if an
897 897 uncaught exception is triggered by your code.
898 898
899 899 For stand-alone use of the feature in your programs which do not use
900 900 IPython at all, put the following lines toward the top of your 'main'
901 901 routine::
902 902
903 903 import sys
904 904 from IPython.core import ultratb
905 905 sys.excepthook = ultratb.FormattedTB(mode='Verbose',
906 906 color_scheme='Linux', call_pdb=1)
907 907
908 908 The mode keyword can be either 'Verbose' or 'Plain', giving either very
909 909 detailed or normal tracebacks respectively. The color_scheme keyword can
910 910 be one of 'NoColor', 'Linux' (default) or 'LightBG'. These are the same
911 911 options which can be set in IPython with ``--colors`` and ``--xmode``.
912 912
913 913 This will give any of your programs detailed, colored tracebacks with
914 914 automatic invocation of pdb.
915 915
916 916
917 917 Extensions for syntax processing
918 918 ================================
919 919
920 920 This isn't for the faint of heart, because the potential for breaking
921 921 things is quite high. But it can be a very powerful and useful feature.
922 922 In a nutshell, you can redefine the way IPython processes the user input
923 923 line to accept new, special extensions to the syntax without needing to
924 924 change any of IPython's own code.
925 925
926 926 In the IPython/extensions directory you will find some examples
927 927 supplied, which we will briefly describe now. These can be used 'as is'
928 928 (and both provide very useful functionality), or you can use them as a
929 929 starting point for writing your own extensions.
930 930
931 931 .. _pasting_with_prompts:
932 932
933 933 Pasting of code starting with Python or IPython prompts
934 934 -------------------------------------------------------
935 935
936 936 IPython is smart enough to filter out input prompts, be they plain Python ones
937 937 (``>>>`` and ``...``) or IPython ones (``In [N]:`` and `` ...:``). You can
938 938 therefore copy and paste from existing interactive sessions without worry.
939 939
940 940 The following is a 'screenshot' of how things work, copying an example from the
941 941 standard Python tutorial::
942 942
943 943 In [1]: >>> # Fibonacci series:
944 944
945 945 In [2]: ... # the sum of two elements defines the next
946 946
947 947 In [3]: ... a, b = 0, 1
948 948
949 949 In [4]: >>> while b < 10:
950 950 ...: ... print b
951 951 ...: ... a, b = b, a+b
952 952 ...:
953 953 1
954 954 1
955 955 2
956 956 3
957 957 5
958 958 8
959 959
960 960 And pasting from IPython sessions works equally well::
961 961
962 962 In [1]: In [5]: def f(x):
963 963 ...: ...: "A simple function"
964 964 ...: ...: return x**2
965 965 ...: ...:
966 966
967 967 In [2]: f(3)
968 968 Out[2]: 9
969 969
970 970 .. _gui_support:
971 971
972 972 GUI event loop support
973 973 ======================
974 974
975 975 .. versionadded:: 0.11
976 976 The ``%gui`` magic and :mod:`IPython.lib.inputhook`.
977 977
978 978 IPython has excellent support for working interactively with Graphical User
979 979 Interface (GUI) toolkits, such as wxPython, PyQt4/PySide, PyGTK and Tk. This is
980 980 implemented using Python's builtin ``PyOSInputHook`` hook. This implementation
981 981 is extremely robust compared to our previous thread-based version. The
982 982 advantages of this are:
983 983
984 984 * GUIs can be enabled and disabled dynamically at runtime.
985 985 * The active GUI can be switched dynamically at runtime.
986 986 * In some cases, multiple GUIs can run simultaneously with no problems.
987 987 * There is a developer API in :mod:`IPython.lib.inputhook` for customizing
988 988 all of these things.
989 989
990 990 For users, enabling GUI event loop integration is simple. You simple use the
991 991 ``%gui`` magic as follows::
992 992
993 993 %gui [GUINAME]
994 994
995 995 With no arguments, ``%gui`` removes all GUI support. Valid ``GUINAME``
996 996 arguments are ``wx``, ``qt``, ``gtk`` and ``tk``.
997 997
998 998 Thus, to use wxPython interactively and create a running :class:`wx.App`
999 999 object, do::
1000 1000
1001 1001 %gui wx
1002 1002
1003 1003 For information on IPython's Matplotlib integration (and the ``pylab`` mode)
1004 1004 see :ref:`this section <matplotlib_support>`.
1005 1005
1006 1006 For developers that want to use IPython's GUI event loop integration in the
1007 1007 form of a library, these capabilities are exposed in library form in the
1008 1008 :mod:`IPython.lib.inputhook` and :mod:`IPython.lib.guisupport` modules.
1009 1009 Interested developers should see the module docstrings for more information,
1010 1010 but there are a few points that should be mentioned here.
1011 1011
1012 1012 First, the ``PyOSInputHook`` approach only works in command line settings
1013 1013 where readline is activated. The integration with various eventloops
1014 1014 is handled somewhat differently (and more simply) when using the standalone
1015 1015 kernel, as in the qtconsole and notebook.
1016 1016
1017 1017 Second, when using the ``PyOSInputHook`` approach, a GUI application should
1018 1018 *not* start its event loop. Instead all of this is handled by the
1019 1019 ``PyOSInputHook``. This means that applications that are meant to be used both
1020 1020 in IPython and as standalone apps need to have special code to detects how the
1021 1021 application is being run. We highly recommend using IPython's support for this.
1022 1022 Since the details vary slightly between toolkits, we point you to the various
1023 1023 examples in our source directory :file:`docs/examples/lib` that demonstrate
1024 1024 these capabilities.
1025 1025
1026 1026 Third, unlike previous versions of IPython, we no longer "hijack" (replace
1027 1027 them with no-ops) the event loops. This is done to allow applications that
1028 1028 actually need to run the real event loops to do so. This is often needed to
1029 1029 process pending events at critical points.
1030 1030
1031 1031 Finally, we also have a number of examples in our source directory
1032 1032 :file:`docs/examples/lib` that demonstrate these capabilities.
1033 1033
1034 1034 PyQt and PySide
1035 1035 ---------------
1036 1036
1037 1037 .. attempt at explanation of the complete mess that is Qt support
1038 1038
1039 1039 When you use ``--gui=qt`` or ``--pylab=qt``, IPython can work with either
1040 1040 PyQt4 or PySide. There are three options for configuration here, because
1041 1041 PyQt4 has two APIs for QString and QVariant - v1, which is the default on
1042 1042 Python 2, and the more natural v2, which is the only API supported by PySide.
1043 1043 v2 is also the default for PyQt4 on Python 3. IPython's code for the QtConsole
1044 1044 uses v2, but you can still use any interface in your code, since the
1045 1045 Qt frontend is in a different process.
1046 1046
1047 1047 The default will be to import PyQt4 without configuration of the APIs, thus
1048 1048 matching what most applications would expect. It will fall back of PySide if
1049 1049 PyQt4 is unavailable.
1050 1050
1051 1051 If specified, IPython will respect the environment variable ``QT_API`` used
1052 1052 by ETS. ETS 4.0 also works with both PyQt4 and PySide, but it requires
1053 1053 PyQt4 to use its v2 API. So if ``QT_API=pyside`` PySide will be used,
1054 1054 and if ``QT_API=pyqt`` then PyQt4 will be used *with the v2 API* for
1055 1055 QString and QVariant, so ETS codes like MayaVi will also work with IPython.
1056 1056
1057 1057 If you launch IPython in pylab mode with ``ipython --pylab=qt``, then IPython
1058 1058 will ask matplotlib which Qt library to use (only if QT_API is *not set*), via
1059 1059 the 'backend.qt4' rcParam. If matplotlib is version 1.0.1 or older, then
1060 1060 IPython will always use PyQt4 without setting the v2 APIs, since neither v2
1061 1061 PyQt nor PySide work.
1062 1062
1063 1063 .. warning::
1064 1064
1065 1065 Note that this means for ETS 4 to work with PyQt4, ``QT_API`` *must* be set
1066 1066 to work with IPython's qt integration, because otherwise PyQt4 will be
1067 1067 loaded in an incompatible mode.
1068 1068
1069 1069 It also means that you must *not* have ``QT_API`` set if you want to
1070 1070 use ``--gui=qt`` with code that requires PyQt4 API v1.
1071 1071
1072 1072
1073 1073 .. _matplotlib_support:
1074 1074
1075 1075 Plotting with matplotlib
1076 1076 ========================
1077 1077
1078 1078 `Matplotlib`_ provides high quality 2D and 3D plotting for Python. Matplotlib
1079 1079 can produce plots on screen using a variety of GUI toolkits, including Tk,
1080 1080 PyGTK, PyQt4 and wxPython. It also provides a number of commands useful for
1081 1081 scientific computing, all with a syntax compatible with that of the popular
1082 1082 Matlab program.
1083 1083
1084 1084 To start IPython with matplotlib support, use the ``--pylab`` switch. If no
1085 1085 arguments are given, IPython will automatically detect your choice of
1086 1086 matplotlib backend. You can also request a specific backend with ``--pylab
1087 1087 backend``, where ``backend`` must be one of: 'tk', 'qt', 'wx', 'gtk', 'osx'.
1088 1088 In the web notebook and Qt console, 'inline' is also a valid backend value,
1089 1089 which produces static figures inlined inside the application window instead of
1090 1090 matplotlib's interactive figures that live in separate windows.
1091 1091
1092 1092 .. _Matplotlib: http://matplotlib.sourceforge.net
1093 1093
1094 1094 .. _interactive_demos:
1095 1095
1096 1096 Interactive demos with IPython
1097 1097 ==============================
1098 1098
1099 1099 IPython ships with a basic system for running scripts interactively in
1100 1100 sections, useful when presenting code to audiences. A few tags embedded
1101 1101 in comments (so that the script remains valid Python code) divide a file
1102 1102 into separate blocks, and the demo can be run one block at a time, with
1103 1103 IPython printing (with syntax highlighting) the block before executing
1104 1104 it, and returning to the interactive prompt after each block. The
1105 1105 interactive namespace is updated after each block is run with the
1106 1106 contents of the demo's namespace.
1107 1107
1108 1108 This allows you to show a piece of code, run it and then execute
1109 1109 interactively commands based on the variables just created. Once you
1110 1110 want to continue, you simply execute the next block of the demo. The
1111 1111 following listing shows the markup necessary for dividing a script into
1112 1112 sections for execution as a demo:
1113 1113
1114 1114 .. literalinclude:: ../../examples/lib/example-demo.py
1115 1115 :language: python
1116 1116
1117 1117 In order to run a file as a demo, you must first make a Demo object out
1118 1118 of it. If the file is named myscript.py, the following code will make a
1119 1119 demo::
1120 1120
1121 1121 from IPython.lib.demo import Demo
1122 1122
1123 1123 mydemo = Demo('myscript.py')
1124 1124
1125 1125 This creates the mydemo object, whose blocks you run one at a time by
1126 1126 simply calling the object with no arguments. If you have autocall active
1127 1127 in IPython (the default), all you need to do is type::
1128 1128
1129 1129 mydemo
1130 1130
1131 1131 and IPython will call it, executing each block. Demo objects can be
1132 1132 restarted, you can move forward or back skipping blocks, re-execute the
1133 1133 last block, etc. Simply use the Tab key on a demo object to see its
1134 1134 methods, and call '?' on them to see their docstrings for more usage
1135 1135 details. In addition, the demo module itself contains a comprehensive
1136 1136 docstring, which you can access via::
1137 1137
1138 1138 from IPython.lib import demo
1139 1139
1140 1140 demo?
1141 1141
1142 1142 Limitations: It is important to note that these demos are limited to
1143 1143 fairly simple uses. In particular, you cannot break up sections within
1144 1144 indented code (loops, if statements, function definitions, etc.)
1145 1145 Supporting something like this would basically require tracking the
1146 1146 internal execution state of the Python interpreter, so only top-level
1147 1147 divisions are allowed. If you want to be able to open an IPython
1148 1148 instance at an arbitrary point in a program, you can use IPython's
1149 1149 embedding facilities, see :func:`IPython.embed` for details.
1150 1150
@@ -1,64 +1,70 b''
1 1 #!/usr/bin/env python
2 2 """IPython release script.
3 3
4 4 This should ONLY be run at real release time.
5 5 """
6 6 from __future__ import print_function
7 7
8 8 from toollib import *
9 from gh_api import post_download
9 10
10 11 # Get main ipython dir, this will raise if it doesn't pass some checks
11 12 ipdir = get_ipdir()
12 13 tooldir = pjoin(ipdir, 'tools')
13 14 distdir = pjoin(ipdir, 'dist')
14 15
15 16 # Where I keep static backups of each release
16 17 ipbackupdir = os.path.expanduser('~/ipython/backup')
17 18
18 19 # Start in main IPython dir
19 20 cd(ipdir)
20 21
21 22 # Load release info
22 23 execfile(pjoin('IPython','core','release.py'))
23 24
24 25 # Build site addresses for file uploads
25 26 release_site = '%s/release/%s' % (archive, version)
26 27 backup_site = '%s/backup/' % archive
27 28
28 29 # Start actual release process
29 30 print()
30 31 print('Releasing IPython')
31 32 print('=================')
32 33 print()
33 34 print('Version:', version)
34 35 print()
35 36 print('Source IPython directory:', ipdir)
36 37 print()
37 38
38 39 # Perform local backup, go to tools dir to run it.
39 40 cd(tooldir)
40 41 sh('./make_tarball.py')
41 42 sh('mv ipython-*.tgz %s' % ipbackupdir)
42 43
43 44 # Build release files
44 45 sh('./build_release %s' % ipdir)
45 46
46 47 # Register with the Python Package Index (PyPI)
47 48 print( 'Registering with PyPI...')
48 49 cd(ipdir)
49 50 sh('./setup.py register')
50 51
51 52 # Upload all files
52 53 sh(sdists + ' upload')
53 54 cd(distdir)
54 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 62 # Make target dir if it doesn't exist
57 63 sh('ssh %s "mkdir -p %s/release/%s" ' % (archive_user, archive_dir, version))
58 64 sh('scp * %s' % release_site)
59 65
60 66 print( 'Uploading backup files...')
61 67 cd(ipbackupdir)
62 68 sh('scp `ls -1tr *tgz | tail -1` %s' % backup_site)
63 69
64 70 print('Done!')
@@ -1,52 +1,66 b''
1 1 """
2 2 build [and upload] Windows IPython releases
3 3
4 4 usage:
5 5
6 6 python tools/release_windows.py [--github] [--pypi]
7 7
8 8 Meant to be run on Windows
9 9
10 10 Requires that you have python and python3 on your PATH
11 11 """
12 12
13 13 import glob
14 14 import os
15 15 import shutil
16 16 import sys
17 17
18 18 from toollib import sh
19 19 try:
20 20 import gh_api
21 21 except ImportError:
22 22 gh_api = None
23 23
24 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 28 pypi = '--pypi' in sys.argv
29 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 47 # deliberately mangle the name,
33 48 # so easy_install doesn't find these and do horrible wrong things
34 v = 3 if py.endswith('3') else 2
35 49 try:
36 50 shutil.rmtree('build')
37 51 except OSError:
38 52 pass
39 for plat in ['win32', 'win-amd64']:
53 for plat,py in plat_py.items():
40 54 cmd = cmd_t.format(**locals())
41 55 sh(cmd)
42 56 orig = glob.glob(os.path.join('dist', 'ipython-*.{plat}.exe'.format(**locals())))[0]
43 57 mangled = orig.replace('.{plat}.exe'.format(**locals()),
44 58 '.py{v}-{plat}.exe'.format(**locals())
45 59 )
46 60 os.rename(orig, mangled)
47 61 if pypi:
48 62 sh(pypi_cmd_t.format(fname=mangled))
49 63 if github and gh_api:
50 64 print ("Uploading %s to GitHub" % mangled)
51 65 desc = "IPython Installer for Python {v}.x on {plat}".format(**locals())
52 66 gh_api.post_download('ipython/ipython', mangled, description=desc)
General Comments 0
You need to be logged in to leave comments. Login now