##// 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")
@@ -477,23 +477,51 b' class Pdb(OldPdb):'
477 do_l = do_list
477 do_l = do_list
478
478
479 def do_pdef(self, arg):
479 def do_pdef(self, arg):
480 """The debugger interface to magic_pdef"""
480 """Print the call signature for any callable object.
481
482 The debugger interface to %pdef"""
481 namespaces = [('Locals', self.curframe.f_locals),
483 namespaces = [('Locals', self.curframe.f_locals),
482 ('Globals', self.curframe.f_globals)]
484 ('Globals', self.curframe.f_globals)]
483 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
485 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
484
486
485 def do_pdoc(self, arg):
487 def do_pdoc(self, arg):
486 """The debugger interface to magic_pdoc"""
488 """Print the docstring for an object.
489
490 The debugger interface to %pdoc."""
487 namespaces = [('Locals', self.curframe.f_locals),
491 namespaces = [('Locals', self.curframe.f_locals),
488 ('Globals', self.curframe.f_globals)]
492 ('Globals', self.curframe.f_globals)]
489 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
493 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
490
494
495 def do_pfile(self, arg):
496 """Print (or run through pager) the file where an object is defined.
497
498 The debugger interface to %pfile.
499 """
500 namespaces = [('Locals', self.curframe.f_locals),
501 ('Globals', self.curframe.f_globals)]
502 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
503
491 def do_pinfo(self, arg):
504 def do_pinfo(self, arg):
492 """The debugger equivalant of ?obj"""
505 """Provide detailed information about an object.
506
507 The debugger interface to %pinfo, i.e., obj?."""
508 namespaces = [('Locals', self.curframe.f_locals),
509 ('Globals', self.curframe.f_globals)]
510 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
511
512 def do_pinfo2(self, arg):
513 """Provide extra detailed information about an object.
514
515 The debugger interface to %pinfo2, i.e., obj??."""
516 namespaces = [('Locals', self.curframe.f_locals),
517 ('Globals', self.curframe.f_globals)]
518 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
519
520 def do_psource(self, arg):
521 """Print (or run through pager) the source code for an object."""
493 namespaces = [('Locals', self.curframe.f_locals),
522 namespaces = [('Locals', self.curframe.f_locals),
494 ('Globals', self.curframe.f_globals)]
523 ('Globals', self.curframe.f_globals)]
495 self.shell.find_line_magic('pinfo')("pinfo %s" % arg,
524 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
496 namespaces=namespaces)
497
525
498 def checkline(self, filename, lineno):
526 def checkline(self, filename, lineno):
499 """Check whether specified line seems to be executable.
527 """Check whether specified line seems to be executable.
@@ -23,8 +23,12 b' import sys'
23 from urllib import urlretrieve
23 from urllib import urlretrieve
24 from urlparse import urlparse
24 from urlparse import urlparse
25
25
26 from IPython.core.error import UsageError
26 from IPython.config.configurable import Configurable
27 from IPython.config.configurable import Configurable
27 from IPython.utils.traitlets import Instance
28 from IPython.utils.traitlets import Instance
29 from IPython.utils.py3compat import PY3
30 if PY3:
31 from imp import reload
28
32
29 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
30 # Main class
34 # Main class
@@ -44,10 +48,11 b' class ExtensionManager(Configurable):'
44 the only argument. You can do anything you want with IPython at
48 the only argument. You can do anything you want with IPython at
45 that point, including defining new magic and aliases, adding new
49 that point, including defining new magic and aliases, adding new
46 components, etc.
50 components, etc.
47
51
48 The :func:`load_ipython_extension` will be called again is you
52 You can also optionaly define an :func:`unload_ipython_extension(ipython)`
49 load or reload the extension again. It is up to the extension
53 function, which will be called if the user unloads or reloads the extension.
50 author to add code to manage that.
54 The extension manager will only call :func:`load_ipython_extension` again
55 if the extension is reloaded.
51
56
52 You can put your extension modules anywhere you want, as long as
57 You can put your extension modules anywhere you want, as long as
53 they can be imported by Python's standard import mechanism. However,
58 they can be imported by Python's standard import mechanism. However,
@@ -63,6 +68,7 b' class ExtensionManager(Configurable):'
63 self.shell.on_trait_change(
68 self.shell.on_trait_change(
64 self._on_ipython_dir_changed, 'ipython_dir'
69 self._on_ipython_dir_changed, 'ipython_dir'
65 )
70 )
71 self.loaded = set()
66
72
67 def __del__(self):
73 def __del__(self):
68 self.shell.on_trait_change(
74 self.shell.on_trait_change(
@@ -80,26 +86,43 b' class ExtensionManager(Configurable):'
80 def load_extension(self, module_str):
86 def load_extension(self, module_str):
81 """Load an IPython extension by its module name.
87 """Load an IPython extension by its module name.
82
88
83 If :func:`load_ipython_extension` returns anything, this function
89 Returns the string "already loaded" if the extension is already loaded,
84 will return that object.
90 "no load function" if the module doesn't have a load_ipython_extension
91 function, or None if it succeeded.
85 """
92 """
93 if module_str in self.loaded:
94 return "already loaded"
95
86 from IPython.utils.syspathcontext import prepended_to_syspath
96 from IPython.utils.syspathcontext import prepended_to_syspath
87
97
88 if module_str not in sys.modules:
98 if module_str not in sys.modules:
89 with prepended_to_syspath(self.ipython_extension_dir):
99 with prepended_to_syspath(self.ipython_extension_dir):
90 __import__(module_str)
100 __import__(module_str)
91 mod = sys.modules[module_str]
101 mod = sys.modules[module_str]
92 return self._call_load_ipython_extension(mod)
102 if self._call_load_ipython_extension(mod):
103 self.loaded.add(module_str)
104 else:
105 return "no load function"
93
106
94 def unload_extension(self, module_str):
107 def unload_extension(self, module_str):
95 """Unload an IPython extension by its module name.
108 """Unload an IPython extension by its module name.
96
109
97 This function looks up the extension's name in ``sys.modules`` and
110 This function looks up the extension's name in ``sys.modules`` and
98 simply calls ``mod.unload_ipython_extension(self)``.
111 simply calls ``mod.unload_ipython_extension(self)``.
112
113 Returns the string "no unload function" if the extension doesn't define
114 a function to unload itself, "not loaded" if the extension isn't loaded,
115 otherwise None.
99 """
116 """
117 if module_str not in self.loaded:
118 return "not loaded"
119
100 if module_str in sys.modules:
120 if module_str in sys.modules:
101 mod = sys.modules[module_str]
121 mod = sys.modules[module_str]
102 self._call_unload_ipython_extension(mod)
122 if self._call_unload_ipython_extension(mod):
123 self.loaded.discard(module_str)
124 else:
125 return "no unload function"
103
126
104 def reload_extension(self, module_str):
127 def reload_extension(self, module_str):
105 """Reload an IPython extension by calling reload.
128 """Reload an IPython extension by calling reload.
@@ -111,21 +134,25 b' class ExtensionManager(Configurable):'
111 """
134 """
112 from IPython.utils.syspathcontext import prepended_to_syspath
135 from IPython.utils.syspathcontext import prepended_to_syspath
113
136
114 with prepended_to_syspath(self.ipython_extension_dir):
137 if (module_str in self.loaded) and (module_str in sys.modules):
115 if module_str in sys.modules:
138 self.unload_extension(module_str)
116 mod = sys.modules[module_str]
139 mod = sys.modules[module_str]
140 with prepended_to_syspath(self.ipython_extension_dir):
117 reload(mod)
141 reload(mod)
118 self._call_load_ipython_extension(mod)
142 if self._call_load_ipython_extension(mod):
119 else:
143 self.loaded.add(module_str)
120 self.load_extension(module_str)
144 else:
145 self.load_extension(module_str)
121
146
122 def _call_load_ipython_extension(self, mod):
147 def _call_load_ipython_extension(self, mod):
123 if hasattr(mod, 'load_ipython_extension'):
148 if hasattr(mod, 'load_ipython_extension'):
124 return mod.load_ipython_extension(self.shell)
149 mod.load_ipython_extension(self.shell)
150 return True
125
151
126 def _call_unload_ipython_extension(self, mod):
152 def _call_unload_ipython_extension(self, mod):
127 if hasattr(mod, 'unload_ipython_extension'):
153 if hasattr(mod, 'unload_ipython_extension'):
128 return mod.unload_ipython_extension(self.shell)
154 mod.unload_ipython_extension(self.shell)
155 return True
129
156
130 def install_extension(self, url, filename=None):
157 def install_extension(self, url, filename=None):
131 """Download and install an IPython extension.
158 """Download and install an IPython extension.
@@ -629,7 +629,7 b' class InteractiveShell(SingletonConfigurable):'
629 # override sys.stdout and sys.stderr themselves, you need to do that
629 # override sys.stdout and sys.stderr themselves, you need to do that
630 # *before* instantiating this class, because io holds onto
630 # *before* instantiating this class, because io holds onto
631 # references to the underlying streams.
631 # references to the underlying streams.
632 if sys.platform == 'win32' and self.has_readline:
632 if (sys.platform == 'win32' or sys.platform == 'cli') and self.has_readline:
633 io.stdout = io.stderr = io.IOStream(self.readline._outputfile)
633 io.stdout = io.stderr = io.IOStream(self.readline._outputfile)
634 else:
634 else:
635 io.stdout = io.IOStream(sys.stdout)
635 io.stdout = io.IOStream(sys.stdout)
@@ -173,11 +173,17 b' class magic_arguments(ArgDecorator):'
173 return func
173 return func
174
174
175
175
176 class argument(ArgDecorator):
176 class ArgMethodWrapper(ArgDecorator):
177 """ Store arguments and keywords to pass to add_argument().
177
178 """
179 Base class to define a wrapper for ArgumentParser method.
180
181 Child class must define either `_method_name` or `add_to_parser`.
178
182
179 Instances also serve to decorate command methods.
180 """
183 """
184
185 _method_name = None
186
181 def __init__(self, *args, **kwds):
187 def __init__(self, *args, **kwds):
182 self.args = args
188 self.args = args
183 self.kwds = kwds
189 self.kwds = kwds
@@ -187,18 +193,31 b' class argument(ArgDecorator):'
187 """
193 """
188 if group is not None:
194 if group is not None:
189 parser = group
195 parser = group
190 parser.add_argument(*self.args, **self.kwds)
196 getattr(parser, self._method_name)(*self.args, **self.kwds)
191 return None
197 return None
192
198
193
199
194 class argument_group(ArgDecorator):
200 class argument(ArgMethodWrapper):
201 """ Store arguments and keywords to pass to add_argument().
202
203 Instances also serve to decorate command methods.
204 """
205 _method_name = 'add_argument'
206
207
208 class defaults(ArgMethodWrapper):
209 """ Store arguments and keywords to pass to set_defaults().
210
211 Instances also serve to decorate command methods.
212 """
213 _method_name = 'set_defaults'
214
215
216 class argument_group(ArgMethodWrapper):
195 """ Store arguments and keywords to pass to add_argument_group().
217 """ Store arguments and keywords to pass to add_argument_group().
196
218
197 Instances also serve to decorate command methods.
219 Instances also serve to decorate command methods.
198 """
220 """
199 def __init__(self, *args, **kwds):
200 self.args = args
201 self.kwds = kwds
202
221
203 def add_to_parser(self, parser, group):
222 def add_to_parser(self, parser, group):
204 """ Add this object's information to the parser.
223 """ Add this object's information to the parser.
@@ -313,7 +313,8 b' Currently the magic system has the following functions:""",'
313 import IPython.utils.rlineimpl as readline
313 import IPython.utils.rlineimpl as readline
314
314
315 if not shell.colors_force and \
315 if not shell.colors_force and \
316 not readline.have_readline and sys.platform == "win32":
316 not readline.have_readline and \
317 (sys.platform == "win32" or sys.platform == "cli"):
317 msg = """\
318 msg = """\
318 Proper color support under MS Windows requires the pyreadline library.
319 Proper color support under MS Windows requires the pyreadline library.
319 You can find it at:
320 You can find it at:
@@ -563,6 +563,11 b' python-profiler package from non-free.""")'
563 return
563 return
564 # if we find a good linenumber, set the breakpoint
564 # if we find a good linenumber, set the breakpoint
565 deb.do_break('%s:%s' % (filename, bp))
565 deb.do_break('%s:%s' % (filename, bp))
566
567 # Mimic Pdb._runscript(...)
568 deb._wait_for_mainpyfile = True
569 deb.mainpyfile = deb.canonic(filename)
570
566 # Start file run
571 # Start file run
567 print "NOTE: Enter 'c' at the",
572 print "NOTE: Enter 'c' at the",
568 print "%s prompt to start your script." % deb.prompt
573 print "%s prompt to start your script." % deb.prompt
@@ -59,14 +59,30 b' class ExtensionMagics(Magics):'
59 """Load an IPython extension by its module name."""
59 """Load an IPython extension by its module name."""
60 if not module_str:
60 if not module_str:
61 raise UsageError('Missing module name.')
61 raise UsageError('Missing module name.')
62 return self.shell.extension_manager.load_extension(module_str)
62 res = self.shell.extension_manager.load_extension(module_str)
63
64 if res == 'already loaded':
65 print "The %s extension is already loaded. To reload it, use:" % module_str
66 print " %reload_ext", module_str
67 elif res == 'no load function':
68 print "The %s module is not an IPython extension." % module_str
63
69
64 @line_magic
70 @line_magic
65 def unload_ext(self, module_str):
71 def unload_ext(self, module_str):
66 """Unload an IPython extension by its module name."""
72 """Unload an IPython extension by its module name.
73
74 Not all extensions can be unloaded, only those which define an
75 ``unload_ipython_extension`` function.
76 """
67 if not module_str:
77 if not module_str:
68 raise UsageError('Missing module name.')
78 raise UsageError('Missing module name.')
69 self.shell.extension_manager.unload_extension(module_str)
79
80 res = self.shell.extension_manager.unload_extension(module_str)
81
82 if res == 'no unload function':
83 print "The %s extension doesn't define how to unload it." % module_str
84 elif res == "not loaded":
85 print "The %s extension is not loaded." % module_str
70
86
71 @line_magic
87 @line_magic
72 def reload_ext(self, module_str):
88 def reload_ext(self, module_str):
@@ -16,10 +16,13 b' from __future__ import print_function'
16 # Stdlib
16 # Stdlib
17 import os
17 import os
18 from io import open as io_open
18 from io import open as io_open
19 from IPython.external.argparse import Action
19
20
20 # Our own packages
21 # Our own packages
21 from IPython.core.error import StdinNotImplementedError
22 from IPython.core.error import StdinNotImplementedError
22 from IPython.core.magic import Magics, magics_class, line_magic
23 from IPython.core.magic import Magics, magics_class, line_magic
24 from IPython.core.magic_arguments import (argument, magic_arguments,
25 parse_argstring)
23 from IPython.testing.skipdoctest import skip_doctest
26 from IPython.testing.skipdoctest import skip_doctest
24 from IPython.utils import io
27 from IPython.utils import io
25
28
@@ -27,16 +30,71 b' from IPython.utils import io'
27 # Magics class implementation
30 # Magics class implementation
28 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
29
32
33
34 _unspecified = object()
35
36
30 @magics_class
37 @magics_class
31 class HistoryMagics(Magics):
38 class HistoryMagics(Magics):
32
39
40 @magic_arguments()
41 @argument(
42 '-n', dest='print_nums', action='store_true', default=False,
43 help="""
44 print line numbers for each input.
45 This feature is only available if numbered prompts are in use.
46 """)
47 @argument(
48 '-o', dest='get_output', action='store_true', default=False,
49 help="also print outputs for each input.")
50 @argument(
51 '-p', dest='pyprompts', action='store_true', default=False,
52 help="""
53 print classic '>>>' python prompts before each input.
54 This is useful for making documentation, and in conjunction
55 with -o, for producing doctest-ready output.
56 """)
57 @argument(
58 '-t', dest='raw', action='store_false', default=True,
59 help="""
60 print the 'translated' history, as IPython understands it.
61 IPython filters your input and converts it all into valid Python
62 source before executing it (things like magics or aliases are turned
63 into function calls, for example). With this option, you'll see the
64 native history instead of the user-entered version: '%%cd /' will be
65 seen as 'get_ipython().magic("%%cd /")' instead of '%%cd /'.
66 """)
67 @argument(
68 '-f', dest='filename',
69 help="""
70 FILENAME: instead of printing the output to the screen, redirect
71 it to the given file. The file is always overwritten, though *when
72 it can*, IPython asks for confirmation first. In particular, running
73 the command 'history -f FILENAME' from the IPython Notebook
74 interface will replace FILENAME even if it already exists *without*
75 confirmation.
76 """)
77 @argument(
78 '-g', dest='pattern', nargs='*', default=None,
79 help="""
80 treat the arg as a glob pattern to search for in (full) history.
81 This includes the saved history (almost all commands ever written).
82 The pattern may contain '?' to match one unknown character and '*'
83 to match any number of unknown characters. Use '%%hist -g' to show
84 full saved history (may be very long).
85 """)
86 @argument(
87 '-l', dest='limit', type=int, nargs='?', default=_unspecified,
88 help="""
89 get the last n lines from all sessions. Specify n as a single
90 arg, or the default is the last 10 lines.
91 """)
92 @argument('range', nargs='*')
33 @skip_doctest
93 @skip_doctest
34 @line_magic
94 @line_magic
35 def history(self, parameter_s = ''):
95 def history(self, parameter_s = ''):
36 """Print input history (_i<n> variables), with most recent last.
96 """Print input history (_i<n> variables), with most recent last.
37
97
38 %history [-o -p -t -n] [-f filename] [range | -g pattern | -l number]
39
40 By default, input history is printed without line numbers so it can be
98 By default, input history is printed without line numbers so it can be
41 directly pasted into an editor. Use -n to show them.
99 directly pasted into an editor. Use -n to show them.
42
100
@@ -52,43 +110,6 b' class HistoryMagics(Magics):'
52
110
53 The same syntax is used by %macro, %save, %edit, %rerun
111 The same syntax is used by %macro, %save, %edit, %rerun
54
112
55 Options:
56
57 -n: print line numbers for each input.
58 This feature is only available if numbered prompts are in use.
59
60 -o: also print outputs for each input.
61
62 -p: print classic '>>>' python prompts before each input. This is
63 useful for making documentation, and in conjunction with -o, for
64 producing doctest-ready output.
65
66 -r: (default) print the 'raw' history, i.e. the actual commands you
67 typed.
68
69 -t: print the 'translated' history, as IPython understands it.
70 IPython filters your input and converts it all into valid Python
71 source before executing it (things like magics or aliases are turned
72 into function calls, for example). With this option, you'll see the
73 native history instead of the user-entered version: '%cd /' will be
74 seen as 'get_ipython().magic("%cd /")' instead of '%cd /'.
75
76 -g: treat the arg as a pattern to grep for in (full) history.
77 This includes the saved history (almost all commands ever written).
78 The pattern may contain '?' to match one unknown character and '*'
79 to match any number of unknown characters. Use '%hist -g' to show
80 full saved history (may be very long).
81
82 -l: get the last n lines from all sessions. Specify n as a single
83 arg, or the default is the last 10 lines.
84
85 -f FILENAME: instead of printing the output to the screen, redirect
86 it to the given file. The file is always overwritten, though *when
87 it can*, IPython asks for confirmation first. In particular, running
88 the command 'history -f FILENAME' from the IPython Notebook
89 interface will replace FILENAME even if it already exists *without*
90 confirmation.
91
92 Examples
113 Examples
93 --------
114 --------
94 ::
115 ::
@@ -100,11 +121,7 b' class HistoryMagics(Magics):'
100
121
101 """
122 """
102
123
103 if not self.shell.displayhook.do_full_cache:
124 args = parse_argstring(self.history, parameter_s)
104 print('This feature is only available if numbered prompts '
105 'are in use.')
106 return
107 opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string')
108
125
109 # For brevity
126 # For brevity
110 history_manager = self.shell.history_manager
127 history_manager = self.shell.history_manager
@@ -116,9 +133,8 b' class HistoryMagics(Magics):'
116 return "%s/%s" % (session, line)
133 return "%s/%s" % (session, line)
117
134
118 # Check if output to specific file was requested.
135 # Check if output to specific file was requested.
119 try:
136 outfname = args.filename
120 outfname = opts['f']
137 if not outfname:
121 except KeyError:
122 outfile = io.stdout # default
138 outfile = io.stdout # default
123 # We don't want to close stdout at the end!
139 # We don't want to close stdout at the end!
124 close_at_end = False
140 close_at_end = False
@@ -135,27 +151,29 b' class HistoryMagics(Magics):'
135 outfile = io_open(outfname, 'w', encoding='utf-8')
151 outfile = io_open(outfname, 'w', encoding='utf-8')
136 close_at_end = True
152 close_at_end = True
137
153
138 print_nums = 'n' in opts
154 print_nums = args.print_nums
139 get_output = 'o' in opts
155 get_output = args.get_output
140 pyprompts = 'p' in opts
156 pyprompts = args.pyprompts
141 # Raw history is the default
157 raw = args.raw
142 raw = not('t' in opts)
143
158
144 pattern = None
159 pattern = None
160 limit = None if args.limit is _unspecified else args.limit
145
161
146 if 'g' in opts: # Glob search
162 if args.pattern is not None:
147 pattern = "*" + args + "*" if args else "*"
163 if args.pattern:
148 hist = history_manager.search(pattern, raw=raw, output=get_output)
164 pattern = "*" + " ".join(args.pattern) + "*"
165 else:
166 pattern = "*"
167 hist = history_manager.search(pattern, raw=raw, output=get_output,
168 n=limit)
149 print_nums = True
169 print_nums = True
150 elif 'l' in opts: # Get 'tail'
170 elif args.limit is not _unspecified:
151 try:
171 n = 10 if limit is None else limit
152 n = int(args)
153 except (ValueError, IndexError):
154 n = 10
155 hist = history_manager.get_tail(n, raw=raw, output=get_output)
172 hist = history_manager.get_tail(n, raw=raw, output=get_output)
156 else:
173 else:
157 if args: # Get history by ranges
174 if args.range: # Get history by ranges
158 hist = history_manager.get_range_by_str(args, raw, get_output)
175 hist = history_manager.get_range_by_str(" ".join(args.range),
176 raw, get_output)
159 else: # Just get history for the current session
177 else: # Just get history for the current session
160 hist = history_manager.get_range(raw=raw, output=get_output)
178 hist = history_manager.get_range(raw=raw, output=get_output)
161
179
@@ -69,7 +69,7 b' class NamespaceMagics(Magics):'
69 @skip_doctest
69 @skip_doctest
70 @line_magic
70 @line_magic
71 def pdef(self, parameter_s='', namespaces=None):
71 def pdef(self, parameter_s='', namespaces=None):
72 """Print the definition header for any callable object.
72 """Print the call signature for any callable object.
73
73
74 If the object is a class, print the constructor information.
74 If the object is a class, print the constructor information.
75
75
@@ -98,7 +98,7 b' class NamespaceMagics(Magics):'
98 self.shell._inspect('psource',parameter_s, namespaces)
98 self.shell._inspect('psource',parameter_s, namespaces)
99
99
100 @line_magic
100 @line_magic
101 def pfile(self, parameter_s=''):
101 def pfile(self, parameter_s='', namespaces=None):
102 """Print (or run through pager) the file where an object is defined.
102 """Print (or run through pager) the file where an object is defined.
103
103
104 The file opens at the line where the object definition begins. IPython
104 The file opens at the line where the object definition begins. IPython
@@ -111,7 +111,7 b' class NamespaceMagics(Magics):'
111 viewer."""
111 viewer."""
112
112
113 # first interpret argument as an object name
113 # first interpret argument as an object name
114 out = self.shell._inspect('pfile',parameter_s)
114 out = self.shell._inspect('pfile',parameter_s, namespaces)
115 # if not, try the input as a filename
115 # if not, try the input as a filename
116 if out == 'not found':
116 if out == 'not found':
117 try:
117 try:
@@ -344,7 +344,7 b' class Inspector:'
344 self.set_active_scheme(scheme)
344 self.set_active_scheme(scheme)
345
345
346 def _getdef(self,obj,oname=''):
346 def _getdef(self,obj,oname=''):
347 """Return the definition header for any callable object.
347 """Return the call signature for any callable object.
348
348
349 If any exception is generated, None is returned instead and the
349 If any exception is generated, None is returned instead and the
350 exception is suppressed."""
350 exception is suppressed."""
@@ -373,7 +373,7 b' class Inspector:'
373 print()
373 print()
374
374
375 def pdef(self, obj, oname=''):
375 def pdef(self, obj, oname=''):
376 """Print the definition header for any callable object.
376 """Print the call signature for any callable object.
377
377
378 If the object is a class, print the constructor information."""
378 If the object is a class, print the constructor information."""
379
379
@@ -96,13 +96,41 b' def doctest_run_option_parser():'
96 In [2]: %run print_argv.py print*.py
96 In [2]: %run print_argv.py print*.py
97 ['print_argv.py']
97 ['print_argv.py']
98
98
99 In [3]: %run print_argv.py print\\*.py
99 In [3]: %run -G print_argv.py print*.py
100 ['print*.py']
100 ['print*.py']
101
101
102 In [4]: %run print_argv.py 'print*.py'
102 """
103
104
105 @dec.skip_win32
106 def doctest_run_option_parser_for_posix():
107 r"""Test option parser in %run (Linux/OSX specific).
108
109 You need double quote to escape glob in POSIX systems:
110
111 In [1]: %run print_argv.py print\\*.py
112 ['print*.py']
113
114 You can't use quote to escape glob in POSIX systems:
115
116 In [2]: %run print_argv.py 'print*.py'
103 ['print_argv.py']
117 ['print_argv.py']
104
118
105 In [5]: %run -G print_argv.py print*.py
119 """
120
121
122 @dec.skip_if_not_win32
123 def doctest_run_option_parser_for_windows():
124 r"""Test option parser in %run (Windows specific).
125
126 In Windows, you can't escape ``*` `by backslash:
127
128 In [1]: %run print_argv.py print\\*.py
129 ['print\\*.py']
130
131 You can use quote to escape glob:
132
133 In [2]: %run print_argv.py 'print*.py'
106 ['print*.py']
134 ['print*.py']
107
135
108 """
136 """
@@ -514,14 +514,8 b' class AutoreloadMagics(Magics):'
514 pass
514 pass
515
515
516
516
517 _loaded = False
518
519
520 def load_ipython_extension(ip):
517 def load_ipython_extension(ip):
521 """Load the extension in IPython."""
518 """Load the extension in IPython."""
522 global _loaded
519 auto_reload = AutoreloadMagics(ip)
523 if not _loaded:
520 ip.register_magics(auto_reload)
524 auto_reload = AutoreloadMagics(ip)
521 ip.set_hook('pre_run_code_hook', auto_reload.pre_run_code_hook)
525 ip.register_magics(auto_reload)
526 ip.set_hook('pre_run_code_hook', auto_reload.pre_run_code_hook)
527 _loaded = True
@@ -273,11 +273,7 b' class CythonMagics(Magics):'
273 html = '\n'.join(l for l in html.splitlines() if not r.match(l))
273 html = '\n'.join(l for l in html.splitlines() if not r.match(l))
274 return html
274 return html
275
275
276 _loaded = False
277
276
278 def load_ipython_extension(ip):
277 def load_ipython_extension(ip):
279 """Load the extension in IPython."""
278 """Load the extension in IPython."""
280 global _loaded
279 ip.register_magics(CythonMagics)
281 if not _loaded:
282 ip.register_magics(CythonMagics)
283 _loaded = True
@@ -362,10 +362,6 b' __doc__ = __doc__.format('
362 )
362 )
363
363
364
364
365 _loaded = False
366 def load_ipython_extension(ip):
365 def load_ipython_extension(ip):
367 """Load the extension in IPython."""
366 """Load the extension in IPython."""
368 global _loaded
367 ip.register_magics(OctaveMagics)
369 if not _loaded:
370 ip.register_magics(OctaveMagics)
371 _loaded = True
@@ -588,10 +588,6 b' __doc__ = __doc__.format('
588 )
588 )
589
589
590
590
591 _loaded = False
592 def load_ipython_extension(ip):
591 def load_ipython_extension(ip):
593 """Load the extension in IPython."""
592 """Load the extension in IPython."""
594 global _loaded
593 ip.register_magics(RMagics)
595 if not _loaded:
596 ip.register_magics(RMagics)
597 _loaded = True
@@ -209,12 +209,6 b' class StoreMagics(Magics):'
209 print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__)
209 print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__)
210
210
211
211
212 _loaded = False
213
214
215 def load_ipython_extension(ip):
212 def load_ipython_extension(ip):
216 """Load the extension in IPython."""
213 """Load the extension in IPython."""
217 global _loaded
214 ip.register_magics(StoreMagics)
218 if not _loaded:
219 ip.register_magics(StoreMagics)
220 _loaded = True
@@ -22,6 +22,7 b' Authors'
22 from __future__ import print_function
22 from __future__ import print_function
23
23
24 import os,sys, atexit
24 import os,sys, atexit
25 import signal
25 import socket
26 import socket
26 from multiprocessing import Process
27 from multiprocessing import Process
27 from getpass import getpass, getuser
28 from getpass import getpass, getuser
@@ -331,9 +332,10 b' def _paramiko_tunnel(lport, rport, server, remoteip, keyfile=None, password=None'
331 except Exception as e:
332 except Exception as e:
332 print ('*** Failed to connect to %s:%d: %r' % (server, port, e))
333 print ('*** Failed to connect to %s:%d: %r' % (server, port, e))
333 sys.exit(1)
334 sys.exit(1)
334
335
335 # print ('Now forwarding port %d to %s:%d ...' % (lport, server, rport))
336 # Don't let SIGINT kill the tunnel subprocess
336
337 signal.signal(signal.SIGINT, signal.SIG_IGN)
338
337 try:
339 try:
338 forward_tunnel(lport, remoteip, rport, client.get_transport())
340 forward_tunnel(lport, remoteip, rport, client.get_transport())
339 except KeyboardInterrupt:
341 except KeyboardInterrupt:
@@ -470,11 +470,14 b' class NotebookApp(BaseIPythonApplication):'
470 ssl_options = None
470 ssl_options = None
471 self.web_app.password = self.password
471 self.web_app.password = self.password
472 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
472 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
473 if ssl_options is None and not self.ip and not (self.read_only and not self.password):
473 if not self.ip:
474 self.log.critical('WARNING: the notebook server is listening on all IP addresses '
474 warning = "WARNING: The notebook server is listening on all IP addresses"
475 'but not using any encryption or authentication. This is highly '
475 if ssl_options is None:
476 'insecure and not recommended.')
476 self.log.critical(warning + " and not using encryption. This"
477
477 "is not recommended.")
478 if not self.password and not self.read_only:
479 self.log.critical(warning + "and not using authentication."
480 "This is highly insecure and not recommended.")
478 success = None
481 success = None
479 for port in random_ports(self.port, self.port_retries+1):
482 for port in random_ports(self.port, self.port_retries+1):
480 try:
483 try:
@@ -50,15 +50,13 b' var IPython = (function (IPython) {'
50 });
50 });
51 };
51 };
52
52
53
54 // typeset with MathJax if MathJax is available
55 Cell.prototype.typeset = function () {
53 Cell.prototype.typeset = function () {
56 if (window.MathJax){
54 if (window.MathJax){
57 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
55 var cell_math = this.element.get(0);
56 MathJax.Hub.Queue(["Typeset",MathJax.Hub,cell_math]);
58 }
57 }
59 };
58 };
60
59
61
62 Cell.prototype.select = function () {
60 Cell.prototype.select = function () {
63 this.element.addClass('ui-widget-content ui-corner-all');
61 this.element.addClass('ui-widget-content ui-corner-all');
64 this.selected = true;
62 this.selected = true;
@@ -123,6 +123,7 b' var IPython = (function (IPython) {'
123 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
123 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
124 // Tab completion.
124 // Tab completion.
125 //Do not trim here because of tooltip
125 //Do not trim here because of tooltip
126 if (editor.somethingSelected()){return false}
126 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
127 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
127 if (pre_cursor.trim() === "") {
128 if (pre_cursor.trim() === "") {
128 // Don't autocomplete if the part of the line before the cursor
129 // Don't autocomplete if the part of the line before the cursor
@@ -64,7 +64,7 b' var IPython = (function (IPython) {'
64
64
65
65
66 Kernel.prototype.restart = function () {
66 Kernel.prototype.restart = function () {
67 $([IPython.events]).trigger({type: 'status_restarting.Kernel', kernel: this});
67 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
68 var that = this;
68 var that = this;
69 if (this.running) {
69 if (this.running) {
70 this.stop_channels();
70 this.stop_channels();
@@ -86,6 +86,7 b' var IPython = (function (IPython) {'
86 this.start_channels();
86 this.start_channels();
87 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply,this);
87 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply,this);
88 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply,this);
88 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply,this);
89 $([IPython.events]).trigger('status_started.Kernel', {kernel: this});
89 };
90 };
90
91
91
92
@@ -245,7 +246,8 b' var IPython = (function (IPython) {'
245 user_expressions : {},
246 user_expressions : {},
246 allow_stdin : false
247 allow_stdin : false
247 };
248 };
248 $.extend(true, content, options)
249 $.extend(true, content, options)
250 $([IPython.events]).trigger('execution_request.Kernel', {kernel: this, content:content});
249 var msg = this._get_msg("execute_request", content);
251 var msg = this._get_msg("execute_request", content);
250 this.shell_channel.send(JSON.stringify(msg));
252 this.shell_channel.send(JSON.stringify(msg));
251 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
253 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
@@ -279,7 +281,7 b' var IPython = (function (IPython) {'
279
281
280 Kernel.prototype.interrupt = function () {
282 Kernel.prototype.interrupt = function () {
281 if (this.running) {
283 if (this.running) {
282 $([IPython.events]).trigger({type: 'status_interrupting.Kernel', kernel: this});
284 $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this});
283 $.post(this.kernel_url + "/interrupt");
285 $.post(this.kernel_url + "/interrupt");
284 };
286 };
285 };
287 };
@@ -312,6 +314,7 b' var IPython = (function (IPython) {'
312
314
313 Kernel.prototype._handle_shell_reply = function (e) {
315 Kernel.prototype._handle_shell_reply = function (e) {
314 reply = $.parseJSON(e.data);
316 reply = $.parseJSON(e.data);
317 $([IPython.events]).trigger('shell_reply.Kernel', {kernel: this, reply:reply});
315 var header = reply.header;
318 var header = reply.header;
316 var content = reply.content;
319 var content = reply.content;
317 var metadata = reply.metadata;
320 var metadata = reply.metadata;
@@ -367,12 +370,12 b' var IPython = (function (IPython) {'
367 }
370 }
368 } else if (msg_type === 'status') {
371 } else if (msg_type === 'status') {
369 if (content.execution_state === 'busy') {
372 if (content.execution_state === 'busy') {
370 $([IPython.events]).trigger({type: 'status_busy.Kernel', kernel: this});
373 $([IPython.events]).trigger('status_busy.Kernel', {kernel: this});
371 } else if (content.execution_state === 'idle') {
374 } else if (content.execution_state === 'idle') {
372 $([IPython.events]).trigger({type: 'status_idle.Kernel', kernel: this});
375 $([IPython.events]).trigger('status_idle.Kernel', {kernel: this});
373 } else if (content.execution_state === 'dead') {
376 } else if (content.execution_state === 'dead') {
374 this.stop_channels();
377 this.stop_channels();
375 $([IPython.events]).trigger({type: 'status_dead.Kernel', kernel: this});
378 $([IPython.events]).trigger('status_dead.Kernel', {kernel: this});
376 };
379 };
377 } else if (msg_type === 'clear_output') {
380 } else if (msg_type === 'clear_output') {
378 var cb = callbacks['clear_output'];
381 var cb = callbacks['clear_output'];
@@ -51,10 +51,10 b' var IPython = (function (IPython) {'
51 },
51 },
52 {
52 {
53 id : 'paste_b',
53 id : 'paste_b',
54 label : 'Paste Cell',
54 label : 'Paste Cell Below',
55 icon : 'ui-icon-clipboard',
55 icon : 'ui-icon-clipboard',
56 callback : function () {
56 callback : function () {
57 IPython.notebook.paste_cell();
57 IPython.notebook.paste_cell_below();
58 }
58 }
59 }
59 }
60 ],'cut_copy_paste');
60 ],'cut_copy_paste');
@@ -14,10 +14,9 b" IPython.namespace('IPython.mathjaxutils');"
14 IPython.mathjaxutils = (function (IPython) {
14 IPython.mathjaxutils = (function (IPython) {
15
15
16 var init = function () {
16 var init = function () {
17 if (window.MathJax) {
17 if (window.MathJax) {
18 // MathJax loaded
18 // MathJax loaded
19 MathJax.Hub.Config({
19 MathJax.Hub.Config({
20 TeX: { equationNumbers: { autoNumber: "AMS", useLabelIds: true } },
21 tex2jax: {
20 tex2jax: {
22 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
21 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
23 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
22 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
@@ -28,6 +27,7 b' IPython.mathjaxutils = (function (IPython) {'
28 styles: {'.MathJax_Display': {"margin": 0}}
27 styles: {'.MathJax_Display': {"margin": 0}}
29 }
28 }
30 });
29 });
30 MathJax.Hub.Configured();
31 } else if (window.mathjax_url != "") {
31 } else if (window.mathjax_url != "") {
32 // Don't have MathJax, but should. Show dialog.
32 // Don't have MathJax, but should. Show dialog.
33 var dialog = $('<div></div>')
33 var dialog = $('<div></div>')
@@ -88,7 +88,6 b' IPython.mathjaxutils = (function (IPython) {'
88 var inline = "$"; // the inline math delimiter
88 var inline = "$"; // the inline math delimiter
89 var blocks, start, end, last, braces; // used in searching for math
89 var blocks, start, end, last, braces; // used in searching for math
90 var math; // stores math until pagedown (Markdown parser) is done
90 var math; // stores math until pagedown (Markdown parser) is done
91 var HUB = MathJax.Hub;
92
91
93 // MATHSPLIT contains the pattern for math delimiters and special symbols
92 // MATHSPLIT contains the pattern for math delimiters and special symbols
94 // needed for searching for math in the text input.
93 // needed for searching for math in the text input.
@@ -102,11 +101,12 b' IPython.mathjaxutils = (function (IPython) {'
102 // math, then push the math string onto the storage array.
101 // math, then push the math string onto the storage array.
103 // The preProcess function is called on all blocks if it has been passed in
102 // The preProcess function is called on all blocks if it has been passed in
104 var process_math = function (i, j, pre_process) {
103 var process_math = function (i, j, pre_process) {
104 var hub = MathJax.Hub;
105 var block = blocks.slice(i, j + 1).join("").replace(/&/g, "&amp;") // use HTML entity for &
105 var block = blocks.slice(i, j + 1).join("").replace(/&/g, "&amp;") // use HTML entity for &
106 .replace(/</g, "&lt;") // use HTML entity for <
106 .replace(/</g, "&lt;") // use HTML entity for <
107 .replace(/>/g, "&gt;") // use HTML entity for >
107 .replace(/>/g, "&gt;") // use HTML entity for >
108 ;
108 ;
109 if (HUB.Browser.isMSIE) {
109 if (hub.Browser.isMSIE) {
110 block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n")
110 block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n")
111 }
111 }
112 while (j > i) {
112 while (j > i) {
@@ -127,6 +127,10 b' IPython.mathjaxutils = (function (IPython) {'
127 // (which will be a paragraph).
127 // (which will be a paragraph).
128 //
128 //
129 var remove_math = function (text) {
129 var remove_math = function (text) {
130 if (!window.MathJax) {
131 return text;
132 }
133
130 start = end = last = null; // for tracking math delimiters
134 start = end = last = null; // for tracking math delimiters
131 math = []; // stores math strings for later
135 math = []; // stores math strings for later
132
136
@@ -216,6 +220,10 b' IPython.mathjaxutils = (function (IPython) {'
216 // and clear the math array (no need to keep it around).
220 // and clear the math array (no need to keep it around).
217 //
221 //
218 var replace_math = function (text) {
222 var replace_math = function (text) {
223 if (!window.MathJax) {
224 return text;
225 }
226
219 text = text.replace(/@@(\d+)@@/g, function (match, n) {
227 text = text.replace(/@@(\d+)@@/g, function (match, n) {
220 return math[n]
228 return math[n]
221 });
229 });
@@ -223,21 +231,11 b' IPython.mathjaxutils = (function (IPython) {'
223 return text;
231 return text;
224 }
232 }
225
233
226 var queue_render = function () {
227 // see https://groups.google.com/forum/?fromgroups=#!topic/mathjax-users/cpwy5eCH1ZQ
228 MathJax.Hub.Queue(
229 ["resetEquationNumbers",MathJax.InputJax.TeX],
230 ["PreProcess",MathJax.Hub],
231 ["Reprocess",MathJax.Hub]
232 );
233 }
234
235 return {
234 return {
236 init : init,
235 init : init,
237 process_math : process_math,
236 process_math : process_math,
238 remove_math : remove_math,
237 remove_math : remove_math,
239 replace_math : replace_math,
238 replace_math : replace_math
240 queue_render : queue_render
241 };
239 };
242
240
243 }(IPython)); No newline at end of file
241 }(IPython));
@@ -128,7 +128,13 b' var IPython = (function (IPython) {'
128 });
128 });
129 this.element.find('#run_all_cells').click(function () {
129 this.element.find('#run_all_cells').click(function () {
130 IPython.notebook.execute_all_cells();
130 IPython.notebook.execute_all_cells();
131 });
131 }).attr('title', 'Run all cells in the notebook');
132 this.element.find('#run_all_cells_above').click(function () {
133 IPython.notebook.execute_cells_above();
134 }).attr('title', 'Run all cells above (but not including) this cell');
135 this.element.find('#run_all_cells_below').click(function () {
136 IPython.notebook.execute_cells_below();
137 }).attr('title', 'Run this cell and all cells below it');
132 this.element.find('#to_code').click(function () {
138 this.element.find('#to_code').click(function () {
133 IPython.notebook.to_code();
139 IPython.notebook.to_code();
134 });
140 });
@@ -22,6 +22,9 b' var IPython = (function (IPython) {'
22 this.next_prompt_number = 1;
22 this.next_prompt_number = 1;
23 this.kernel = null;
23 this.kernel = null;
24 this.clipboard = null;
24 this.clipboard = null;
25 this.undelete_backup = null;
26 this.undelete_index = null;
27 this.undelete_below = false;
25 this.paste_enabled = false;
28 this.paste_enabled = false;
26 this.dirty = false;
29 this.dirty = false;
27 this.metadata = {};
30 this.metadata = {};
@@ -139,8 +142,8 b' var IPython = (function (IPython) {'
139 that.control_key_active = false;
142 that.control_key_active = false;
140 return false;
143 return false;
141 } else if (event.which === 86 && that.control_key_active) {
144 } else if (event.which === 86 && that.control_key_active) {
142 // Paste selected cell = v
145 // Paste below selected cell = v
143 that.paste_cell();
146 that.paste_cell_below();
144 that.control_key_active = false;
147 that.control_key_active = false;
145 return false;
148 return false;
146 } else if (event.which === 68 && that.control_key_active) {
149 } else if (event.which === 68 && that.control_key_active) {
@@ -257,6 +260,11 b' var IPython = (function (IPython) {'
257 IPython.quick_help.show_keyboard_shortcuts();
260 IPython.quick_help.show_keyboard_shortcuts();
258 that.control_key_active = false;
261 that.control_key_active = false;
259 return false;
262 return false;
263 } else if (event.which === 90 && that.control_key_active) {
264 // Undo last cell delete = z
265 that.undelete();
266 that.control_key_active = false;
267 return false;
260 } else if (that.control_key_active) {
268 } else if (that.control_key_active) {
261 that.control_key_active = false;
269 that.control_key_active = false;
262 return true;
270 return true;
@@ -536,13 +544,19 b' var IPython = (function (IPython) {'
536
544
537 Notebook.prototype.delete_cell = function (index) {
545 Notebook.prototype.delete_cell = function (index) {
538 var i = this.index_or_selected(index);
546 var i = this.index_or_selected(index);
547 var cell = this.get_selected_cell();
548 this.undelete_backup = cell.toJSON();
539 if (this.is_valid_cell_index(i)) {
549 if (this.is_valid_cell_index(i)) {
540 var ce = this.get_cell_element(i);
550 var ce = this.get_cell_element(i);
541 ce.remove();
551 ce.remove();
542 if (i === (this.ncells())) {
552 if (i === (this.ncells())) {
543 this.select(i-1);
553 this.select(i-1);
554 this.undelete_index = i - 1;
555 this.undelete_below = true;
544 } else {
556 } else {
545 this.select(i);
557 this.select(i);
558 this.undelete_index = i;
559 this.undelete_below = false;
546 };
560 };
547 this.dirty = true;
561 this.dirty = true;
548 };
562 };
@@ -560,6 +574,11 b' var IPython = (function (IPython) {'
560 // index = cell index or undefined to insert below selected
574 // index = cell index or undefined to insert below selected
561 index = this.index_or_selected(index);
575 index = this.index_or_selected(index);
562 var cell = null;
576 var cell = null;
577 // This is intentionally < rather than <= for the sake of more
578 // sensible behavior in some cases.
579 if (this.undelete_index !== null && index < this.undelete_index) {
580 this.undelete_index = this.undelete_index + 1;
581 }
563 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
582 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
564 if (type === 'code') {
583 if (type === 'code') {
565 cell = new IPython.CodeCell(this.kernel);
584 cell = new IPython.CodeCell(this.kernel);
@@ -594,6 +613,9 b' var IPython = (function (IPython) {'
594 // index = cell index or undefined to insert above selected
613 // index = cell index or undefined to insert above selected
595 index = this.index_or_selected(index);
614 index = this.index_or_selected(index);
596 var cell = null;
615 var cell = null;
616 if (this.undelete_index !== null && index <= this.undelete_index) {
617 this.undelete_index = this.undelete_index + 1;
618 }
597 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
619 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
598 if (type === 'code') {
620 if (type === 'code') {
599 cell = new IPython.CodeCell(this.kernel);
621 cell = new IPython.CodeCell(this.kernel);
@@ -756,8 +778,8 b' var IPython = (function (IPython) {'
756 Notebook.prototype.enable_paste = function () {
778 Notebook.prototype.enable_paste = function () {
757 var that = this;
779 var that = this;
758 if (!this.paste_enabled) {
780 if (!this.paste_enabled) {
759 $('#paste_cell').removeClass('ui-state-disabled')
781 $('#paste_cell_replace').removeClass('ui-state-disabled')
760 .on('click', function () {that.paste_cell();});
782 .on('click', function () {that.paste_cell_replace();});
761 $('#paste_cell_above').removeClass('ui-state-disabled')
783 $('#paste_cell_above').removeClass('ui-state-disabled')
762 .on('click', function () {that.paste_cell_above();});
784 .on('click', function () {that.paste_cell_above();});
763 $('#paste_cell_below').removeClass('ui-state-disabled')
785 $('#paste_cell_below').removeClass('ui-state-disabled')
@@ -769,7 +791,7 b' var IPython = (function (IPython) {'
769
791
770 Notebook.prototype.disable_paste = function () {
792 Notebook.prototype.disable_paste = function () {
771 if (this.paste_enabled) {
793 if (this.paste_enabled) {
772 $('#paste_cell').addClass('ui-state-disabled').off('click');
794 $('#paste_cell_replace').addClass('ui-state-disabled').off('click');
773 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
795 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
774 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
796 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
775 this.paste_enabled = false;
797 this.paste_enabled = false;
@@ -789,7 +811,7 b' var IPython = (function (IPython) {'
789 };
811 };
790
812
791
813
792 Notebook.prototype.paste_cell = function () {
814 Notebook.prototype.paste_cell_replace = function () {
793 if (this.clipboard !== null && this.paste_enabled) {
815 if (this.clipboard !== null && this.paste_enabled) {
794 var cell_data = this.clipboard;
816 var cell_data = this.clipboard;
795 var new_cell = this.insert_cell_above(cell_data.cell_type);
817 var new_cell = this.insert_cell_above(cell_data.cell_type);
@@ -818,6 +840,33 b' var IPython = (function (IPython) {'
818 };
840 };
819 };
841 };
820
842
843 // Cell undelete
844
845 Notebook.prototype.undelete = function() {
846 if (this.undelete_backup !== null && this.undelete_index !== null) {
847 var current_index = this.get_selected_index();
848 if (this.undelete_index < current_index) {
849 current_index = current_index + 1;
850 }
851 if (this.undelete_index >= this.ncells()) {
852 this.select(this.ncells() - 1);
853 }
854 else {
855 this.select(this.undelete_index);
856 }
857 var cell_data = this.undelete_backup;
858 var new_cell = null;
859 if (this.undelete_below) {
860 new_cell = this.insert_cell_below(cell_data.cell_type);
861 } else {
862 new_cell = this.insert_cell_above(cell_data.cell_type);
863 }
864 new_cell.fromJSON(cell_data);
865 this.select(current_index);
866 this.undelete_backup = null;
867 this.undelete_index = null;
868 }
869 }
821
870
822 // Split/merge
871 // Split/merge
823
872
@@ -1049,13 +1098,25 b' var IPython = (function (IPython) {'
1049 };
1098 };
1050
1099
1051
1100
1101 Notebook.prototype.execute_cells_below = function () {
1102 this.execute_cell_range(this.get_selected_index(), this.ncells());
1103 that.scroll_to_bottom();
1104 };
1105
1106 Notebook.prototype.execute_cells_above = function () {
1107 this.execute_cell_range(0, this.get_selected_index());
1108 };
1109
1052 Notebook.prototype.execute_all_cells = function () {
1110 Notebook.prototype.execute_all_cells = function () {
1053 var ncells = this.ncells();
1111 this.execute_cell_range(0, this.ncells());
1054 for (var i=0; i<ncells; i++) {
1112 that.scroll_to_bottom();
1113 };
1114
1115 Notebook.prototype.execute_cell_range = function (start, end) {
1116 for (var i=start; i<end; i++) {
1055 this.select(i);
1117 this.select(i);
1056 this.execute_selected_cell({add_new:false});
1118 this.execute_selected_cell({add_new:false});
1057 };
1119 };
1058 this.scroll_to_bottom();
1059 };
1120 };
1060
1121
1061 // Persistance and loading
1122 // Persistance and loading
@@ -379,8 +379,10 b' var IPython = (function (IPython) {'
379 OutputArea.prototype.append_text = function (data, element, extra_class) {
379 OutputArea.prototype.append_text = function (data, element, extra_class) {
380 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_text");
380 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_text");
381 // escape ANSI & HTML specials in plaintext:
381 // escape ANSI & HTML specials in plaintext:
382 data = utils.wrapUrls(data);
382 data = utils.fixConsole(data);
383 data = utils.fixConsole(data);
383 data = utils.fixCarriageReturn(data);
384 data = utils.fixCarriageReturn(data);
385 data = utils.autoLinkUrls(data);
384 if (extra_class){
386 if (extra_class){
385 toinsert.addClass(extra_class);
387 toinsert.addClass(extra_class);
386 }
388 }
@@ -33,6 +33,7 b' var IPython = (function (IPython) {'
33 {key: 'Ctrl-m c', help: 'copy cell'},
33 {key: 'Ctrl-m c', help: 'copy cell'},
34 {key: 'Ctrl-m v', help: 'paste cell'},
34 {key: 'Ctrl-m v', help: 'paste cell'},
35 {key: 'Ctrl-m d', help: 'delete cell'},
35 {key: 'Ctrl-m d', help: 'delete cell'},
36 {key: 'Ctrl-m z', help: 'undo last cell deletion'},
36 {key: 'Ctrl-m a', help: 'insert cell above'},
37 {key: 'Ctrl-m a', help: 'insert cell above'},
37 {key: 'Ctrl-m b', help: 'insert cell below'},
38 {key: 'Ctrl-m b', help: 'insert cell below'},
38 {key: 'Ctrl-m o', help: 'toggle output'},
39 {key: 'Ctrl-m o', help: 'toggle output'},
@@ -221,11 +221,9 b' var IPython = (function (IPython) {'
221 if (this.rendered === false) {
221 if (this.rendered === false) {
222 var text = this.get_text();
222 var text = this.get_text();
223 if (text === "") { text = this.placeholder; }
223 if (text === "") { text = this.placeholder; }
224
225 text = IPython.mathjaxutils.remove_math(text)
224 text = IPython.mathjaxutils.remove_math(text)
226 var html = IPython.markdown_converter.makeHtml(text);
225 var html = IPython.markdown_converter.makeHtml(text);
227 html = IPython.mathjaxutils.replace_math(html)
226 html = IPython.mathjaxutils.replace_math(html)
228
229 try {
227 try {
230 this.set_rendered(html);
228 this.set_rendered(html);
231 } catch (e) {
229 } catch (e) {
@@ -235,7 +233,6 b' var IPython = (function (IPython) {'
235 "Error rendering Markdown!<br/>" + e.toString())
233 "Error rendering Markdown!<br/>" + e.toString())
236 );
234 );
237 }
235 }
238 this.typeset()
239 this.element.find('div.text_cell_input').hide();
236 this.element.find('div.text_cell_input').hide();
240 this.element.find("div.text_cell_render").show();
237 this.element.find("div.text_cell_render").show();
241 var code_snippets = this.element.find("pre > code");
238 var code_snippets = this.element.find("pre > code");
@@ -250,8 +247,7 b' var IPython = (function (IPython) {'
250
247
251 return '<code class="prettyprint">' + code + '</code>';
248 return '<code class="prettyprint">' + code + '</code>';
252 });
249 });
253
250 this.typeset()
254 IPython.mathjaxutils.queue_render()
255 this.rendered = true;
251 this.rendered = true;
256 }
252 }
257 };
253 };
@@ -195,11 +195,30 b' IPython.utils = (function (IPython) {'
195 tmp = txt;
195 tmp = txt;
196 do {
196 do {
197 txt = tmp;
197 txt = tmp;
198 tmp = txt.replace(/^.*\r(?!\n)/gm, '');
198 tmp = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
199 tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
199 } while (tmp.length < txt.length);
200 } while (tmp.length < txt.length);
200 return txt;
201 return txt;
201 }
202 }
202
203
204 // Locate URLs in plain text and wrap them in spaces so that they can be
205 // better picked out by autoLinkUrls even after the text has been
206 // converted to HTML
207 function wrapUrls(txt) {
208 // Note this regexp is a modified version of one from
209 // Markdown.Converter For now it only supports http(s) and ftp URLs,
210 // but could easily support others (though file:// should maybe be
211 // avoided)
212 var url_re = /(^|\W)(https?|ftp)(:\/\/[-A-Z0-9+&@#\/%?=~_|\[\]\(\)!:,\.;]*[-A-Z0-9+&@#\/%=~_|\[\]])($|\W)/gi;
213 return txt.replace(url_re, "$1 $2$3 $4");
214 }
215
216 // Locate a URL with spaces around it and convert that to a anchor tag
217 function autoLinkUrls(txt) {
218 return txt.replace(/ ((https?|ftp):[^'">\s]+) /gi,
219 "<a target=\"_blank\" href=\"$1\">$1</a>");
220 }
221
203 grow = function(element) {
222 grow = function(element) {
204 // Grow the cell by hand. This is used upon reloading from JSON, when the
223 // Grow the cell by hand. This is used upon reloading from JSON, when the
205 // autogrow handler is not called.
224 // autogrow handler is not called.
@@ -261,6 +280,8 b' IPython.utils = (function (IPython) {'
261 keycodes : keycodes,
280 keycodes : keycodes,
262 grow : grow,
281 grow : grow,
263 fixCarriageReturn : fixCarriageReturn,
282 fixCarriageReturn : fixCarriageReturn,
283 wrapUrls : wrapUrls,
284 autoLinkUrls : autoLinkUrls,
264 points_to_pixels : points_to_pixels
285 points_to_pixels : points_to_pixels
265 };
286 };
266
287
@@ -2,7 +2,7 b''
2 {% block stylesheet %}
2 {% block stylesheet %}
3
3
4 {% if mathjax_url %}
4 {% if mathjax_url %}
5 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML" charset="utf-8"></script>
5 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
6 {% end %}
6 {% end %}
7 <script type="text/javascript">
7 <script type="text/javascript">
8 // MathJax disabled, set as null to distingish from *missing* MathJax,
8 // MathJax disabled, set as null to distingish from *missing* MathJax,
@@ -75,9 +75,9 b' data-notebook-id={{notebook_id}}'
75 <ul>
75 <ul>
76 <li id="cut_cell"><a href="#">Cut Cell</a></li>
76 <li id="cut_cell"><a href="#">Cut Cell</a></li>
77 <li id="copy_cell"><a href="#">Copy Cell</a></li>
77 <li id="copy_cell"><a href="#">Copy Cell</a></li>
78 <li id="paste_cell" class="ui-state-disabled"><a href="#">Paste Cell</a></li>
79 <li id="paste_cell_above" class="ui-state-disabled"><a href="#">Paste Cell Above</a></li>
78 <li id="paste_cell_above" class="ui-state-disabled"><a href="#">Paste Cell Above</a></li>
80 <li id="paste_cell_below" class="ui-state-disabled"><a href="#">Paste Cell Below</a></li>
79 <li id="paste_cell_below" class="ui-state-disabled"><a href="#">Paste Cell Below</a></li>
80 <li id="paste_cell_replace" class="ui-state-disabled"><a href="#">Paste Cell &amp; Replace</a></li>
81 <li id="delete_cell"><a href="#">Delete</a></li>
81 <li id="delete_cell"><a href="#">Delete</a></li>
82 <hr/>
82 <hr/>
83 <li id="split_cell"><a href="#">Split Cell</a></li>
83 <li id="split_cell"><a href="#">Split Cell</a></li>
@@ -108,6 +108,8 b' data-notebook-id={{notebook_id}}'
108 <li id="run_cell"><a href="#">Run</a></li>
108 <li id="run_cell"><a href="#">Run</a></li>
109 <li id="run_cell_in_place"><a href="#">Run in Place</a></li>
109 <li id="run_cell_in_place"><a href="#">Run in Place</a></li>
110 <li id="run_all_cells"><a href="#">Run All</a></li>
110 <li id="run_all_cells"><a href="#">Run All</a></li>
111 <li id="run_all_cells_above"><a href="#">Run All Above</a></li>
112 <li id="run_all_cells_below"><a href="#">Run All Below</a></li>
111 <hr/>
113 <hr/>
112 <li id="to_code"><a href="#">Code</a></li>
114 <li id="to_code"><a href="#">Code</a></li>
113 <li id="to_markdown"><a href="#">Markdown </a></li>
115 <li id="to_markdown"><a href="#">Markdown </a></li>
@@ -3,7 +3,7 b''
3 {% block stylesheet %}
3 {% block stylesheet %}
4
4
5 {% if mathjax_url %}
5 {% if mathjax_url %}
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML" charset="utf-8"></script>
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
7 {% end %}
7 {% end %}
8 <script type="text/javascript">
8 <script type="text/javascript">
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
@@ -268,7 +268,6 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
268 self._continuation_prompt = '> '
268 self._continuation_prompt = '> '
269 self._continuation_prompt_html = None
269 self._continuation_prompt_html = None
270 self._executing = False
270 self._executing = False
271 self._filter_drag = False
272 self._filter_resize = False
271 self._filter_resize = False
273 self._html_exporter = HtmlExporter(self._control)
272 self._html_exporter = HtmlExporter(self._control)
274 self._input_buffer_executing = ''
273 self._input_buffer_executing = ''
@@ -343,7 +342,51 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
343 triggered=self.reset_font)
342 triggered=self.reset_font)
344 self.addAction(self.reset_font_size)
343 self.addAction(self.reset_font_size)
345
344
345 # Accept drag and drop events here. Drops were already turned off
346 # in self._control when that widget was created.
347 self.setAcceptDrops(True)
346
348
349 #---------------------------------------------------------------------------
350 # Drag and drop support
351 #---------------------------------------------------------------------------
352
353 def dragEnterEvent(self, e):
354 if e.mimeData().hasUrls():
355 # The link action should indicate to that the drop will insert
356 # the file anme.
357 e.setDropAction(QtCore.Qt.LinkAction)
358 e.accept()
359 elif e.mimeData().hasText():
360 # By changing the action to copy we don't need to worry about
361 # the user accidentally moving text around in the widget.
362 e.setDropAction(QtCore.Qt.CopyAction)
363 e.accept()
364
365 def dragMoveEvent(self, e):
366 if e.mimeData().hasUrls():
367 pass
368 elif e.mimeData().hasText():
369 cursor = self._control.cursorForPosition(e.pos())
370 if self._in_buffer(cursor.position()):
371 e.setDropAction(QtCore.Qt.CopyAction)
372 self._control.setTextCursor(cursor)
373 else:
374 e.setDropAction(QtCore.Qt.IgnoreAction)
375 e.accept()
376
377 def dropEvent(self, e):
378 if e.mimeData().hasUrls():
379 self._keep_cursor_in_buffer()
380 cursor = self._control.textCursor()
381 filenames = [url.toLocalFile() for url in e.mimeData().urls()]
382 text = ', '.join("'" + f.replace("'", "'\"'\"'") + "'"
383 for f in filenames)
384 self._insert_plain_text_into_buffer(cursor, text)
385 elif e.mimeData().hasText():
386 cursor = self._control.cursorForPosition(e.pos())
387 if self._in_buffer(cursor.position()):
388 text = e.mimeData().text()
389 self._insert_plain_text_into_buffer(cursor, text)
347
390
348 def eventFilter(self, obj, event):
391 def eventFilter(self, obj, event):
349 """ Reimplemented to ensure a console-like behavior in the underlying
392 """ Reimplemented to ensure a console-like behavior in the underlying
@@ -392,39 +435,6 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
392 event.key() in self._shortcuts:
435 event.key() in self._shortcuts:
393 event.accept()
436 event.accept()
394
437
395 # Ensure that drags are safe. The problem is that the drag starting
396 # logic, which determines whether the drag is a Copy or Move, is locked
397 # down in QTextControl. If the widget is editable, which it must be if
398 # we're not executing, the drag will be a Move. The following hack
399 # prevents QTextControl from deleting the text by clearing the selection
400 # when a drag leave event originating from this widget is dispatched.
401 # The fact that we have to clear the user's selection is unfortunate,
402 # but the alternative--trying to prevent Qt from using its hardwired
403 # drag logic and writing our own--is worse.
404 elif etype == QtCore.QEvent.DragEnter and \
405 obj == self._control.viewport() and \
406 event.source() == self._control.viewport():
407 self._filter_drag = True
408 elif etype == QtCore.QEvent.DragLeave and \
409 obj == self._control.viewport() and \
410 self._filter_drag:
411 cursor = self._control.textCursor()
412 cursor.clearSelection()
413 self._control.setTextCursor(cursor)
414 self._filter_drag = False
415
416 # Ensure that drops are safe.
417 elif etype == QtCore.QEvent.Drop and obj == self._control.viewport():
418 cursor = self._control.cursorForPosition(event.pos())
419 if self._in_buffer(cursor.position()):
420 text = event.mimeData().text()
421 self._insert_plain_text_into_buffer(cursor, text)
422
423 # Qt is expecting to get something here--drag and drop occurs in its
424 # own event loop. Send a DragLeave event to end it.
425 QtGui.qApp.sendEvent(obj, QtGui.QDragLeaveEvent())
426 return True
427
428 # Handle scrolling of the vsplit pager. This hack attempts to solve
438 # Handle scrolling of the vsplit pager. This hack attempts to solve
429 # problems with tearing of the help text inside the pager window. This
439 # problems with tearing of the help text inside the pager window. This
430 # happens only on Mac OS X with both PySide and PyQt. This fix isn't
440 # happens only on Mac OS X with both PySide and PyQt. This fix isn't
@@ -1035,8 +1045,12 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1035 control.setAcceptRichText(False)
1045 control.setAcceptRichText(False)
1036 control.setMouseTracking(True)
1046 control.setMouseTracking(True)
1037
1047
1048 # Prevent the widget from handling drops, as we already provide
1049 # the logic in this class.
1050 control.setAcceptDrops(False)
1051
1038 # Install event filters. The filter on the viewport is needed for
1052 # Install event filters. The filter on the viewport is needed for
1039 # mouse events and drag events.
1053 # mouse events.
1040 control.installEventFilter(self)
1054 control.installEventFilter(self)
1041 control.viewport().installEventFilter(self)
1055 control.viewport().installEventFilter(self)
1042
1056
@@ -1773,6 +1787,32 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):'
1773 else:
1787 else:
1774 self._append_plain_text(text)
1788 self._append_plain_text(text)
1775
1789
1790 def _set_paging(self, paging):
1791 """
1792 Change the pager to `paging` style.
1793
1794 XXX: currently, this is limited to switching between 'hsplit' and
1795 'vsplit'.
1796
1797 Parameters:
1798 -----------
1799 paging : string
1800 Either "hsplit", "vsplit", or "inside"
1801 """
1802 if self._splitter is None:
1803 raise NotImplementedError("""can only switch if --paging=hsplit or
1804 --paging=vsplit is used.""")
1805 if paging == 'hsplit':
1806 self._splitter.setOrientation(QtCore.Qt.Horizontal)
1807 elif paging == 'vsplit':
1808 self._splitter.setOrientation(QtCore.Qt.Vertical)
1809 elif paging == 'inside':
1810 raise NotImplementedError("""switching to 'inside' paging not
1811 supported yet.""")
1812 else:
1813 raise ValueError("unknown paging method '%s'" % paging)
1814 self.paging = paging
1815
1776 def _prompt_finished(self):
1816 def _prompt_finished(self):
1777 """ Called immediately after a prompt is finished, i.e. when some input
1817 """ Called immediately after a prompt is finished, i.e. when some input
1778 will be processed and a new prompt displayed.
1818 will be processed and a new prompt displayed.
@@ -528,7 +528,25 b' class MainWindow(QtGui.QMainWindow):'
528 statusTip="Clear the console",
528 statusTip="Clear the console",
529 triggered=self.clear_magic_active_frontend)
529 triggered=self.clear_magic_active_frontend)
530 self.add_menu_action(self.view_menu, self.clear_action)
530 self.add_menu_action(self.view_menu, self.clear_action)
531
531
532 self.pager_menu = self.view_menu.addMenu("&Pager")
533
534 hsplit_action = QtGui.QAction(".. &Horizontal Split",
535 self,
536 triggered=lambda: self.set_paging_active_frontend('hsplit'))
537
538 vsplit_action = QtGui.QAction(" : &Vertical Split",
539 self,
540 triggered=lambda: self.set_paging_active_frontend('vsplit'))
541
542 inside_action = QtGui.QAction(" &Inside Pager",
543 self,
544 triggered=lambda: self.set_paging_active_frontend('inside'))
545
546 self.pager_menu.addAction(hsplit_action)
547 self.pager_menu.addAction(vsplit_action)
548 self.pager_menu.addAction(inside_action)
549
532 def init_kernel_menu(self):
550 def init_kernel_menu(self):
533 self.kernel_menu = self.menuBar().addMenu("&Kernel")
551 self.kernel_menu = self.menuBar().addMenu("&Kernel")
534 # Qt on OSX maps Ctrl to Cmd, and Meta to Ctrl
552 # Qt on OSX maps Ctrl to Cmd, and Meta to Ctrl
@@ -829,6 +847,9 b' class MainWindow(QtGui.QMainWindow):'
829 self.maximizeAct.setEnabled(True)
847 self.maximizeAct.setEnabled(True)
830 self.minimizeAct.setEnabled(True)
848 self.minimizeAct.setEnabled(True)
831
849
850 def set_paging_active_frontend(self, paging):
851 self.active_frontend._set_paging(paging)
852
832 def close_active_frontend(self):
853 def close_active_frontend(self):
833 self.close_tab(self.active_frontend)
854 self.close_tab(self.active_frontend)
834
855
@@ -1,7 +1,8 b''
1 """Various display related classes.
1 """Various display related classes.
2
2
3 Authors : MinRK, gregcaporaso
3 Authors : MinRK, gregcaporaso, dannystaple
4 """
4 """
5 import urllib
5
6
6 from os.path import exists, isfile, splitext, abspath, join, isdir, walk
7 from os.path import exists, isfile, splitext, abspath, join, isdir, walk
7
8
@@ -17,24 +18,40 b' class YouTubeVideo(object):'
17
18
18 vid = YouTubeVideo("foo")
19 vid = YouTubeVideo("foo")
19 display(vid)
20 display(vid)
21
22 To start from 30 seconds:
23
24 vid = YouTubeVideo("abc", start=30)
25 display(vid)
26
27 To calculate seconds from time as hours, minutes, seconds use:
28 start=int(timedelta(hours=1, minutes=46, seconds=40).total_seconds())
29
30 Other parameters can be provided as documented at
31 https://developers.google.com/youtube/player_parameters#parameter-subheader
20 """
32 """
21
33
22 def __init__(self, id, width=400, height=300):
34 def __init__(self, id, width=400, height=300, **kwargs):
23 self.id = id
35 self.id = id
24 self.width = width
36 self.width = width
25 self.height = height
37 self.height = height
38 self.params = kwargs
26
39
27 def _repr_html_(self):
40 def _repr_html_(self):
28 """return YouTube embed iframe for this video id"""
41 """return YouTube embed iframe for this video id"""
42 if self.params:
43 params = "?" + urllib.urlencode(self.params)
44 else:
45 params = ""
29 return """
46 return """
30 <iframe
47 <iframe
31 width="%i"
48 width="%i"
32 height="%i"
49 height="%i"
33 src="http://www.youtube.com/embed/%s"
50 src="http://www.youtube.com/embed/%s%s"
34 frameborder="0"
51 frameborder="0"
35 allowfullscreen
52 allowfullscreen
36 ></iframe>
53 ></iframe>
37 """%(self.width, self.height, self.id)
54 """ % (self.width, self.height, self.id, params)
38
55
39 class FileLink(object):
56 class FileLink(object):
40 """Class for embedding a local file link in an IPython session, based on path
57 """Class for embedding a local file link in an IPython session, based on path
@@ -240,4 +257,4 b' class FileLinks(FileLink):'
240 """
257 """
241 result_lines = []
258 result_lines = []
242 walk(self.path, self.terminal_display_formatter, result_lines)
259 walk(self.path, self.terminal_display_formatter, result_lines)
243 return '\n'.join(result_lines) No newline at end of file
260 return '\n'.join(result_lines)
@@ -85,11 +85,33 b' def create_inputhook_qt4(mgr, app=None):'
85 return 0
85 return 0
86 app.processEvents(QtCore.QEventLoop.AllEvents, 300)
86 app.processEvents(QtCore.QEventLoop.AllEvents, 300)
87 if not stdin_ready():
87 if not stdin_ready():
88 # Generally a program would run QCoreApplication::exec()
89 # from main() to enter and process the Qt event loop until
90 # quit() or exit() is called and the program terminates.
91 #
92 # For our input hook integration, we need to repeatedly
93 # enter and process the Qt event loop for only a short
94 # amount of time (say 50ms) to ensure that Python stays
95 # responsive to other user inputs.
96 #
97 # A naive approach would be to repeatedly call
98 # QCoreApplication::exec(), using a timer to quit after a
99 # short amount of time. Unfortunately, QCoreApplication
100 # emits an aboutToQuit signal before stopping, which has
101 # the undesirable effect of closing all modal windows.
102 #
103 # To work around this problem, we instead create a
104 # QEventLoop and call QEventLoop::exec(). Other than
105 # setting some state variables which do not seem to be
106 # used anywhere, the only thing QCoreApplication adds is
107 # the aboutToQuit signal which is precisely what we are
108 # trying to avoid.
88 timer = QtCore.QTimer()
109 timer = QtCore.QTimer()
89 timer.timeout.connect(app.quit)
110 event_loop = QtCore.QEventLoop()
111 timer.timeout.connect(event_loop.quit)
90 while not stdin_ready():
112 while not stdin_ready():
91 timer.start(50)
113 timer.start(50)
92 app.exec_()
114 event_loop.exec_()
93 timer.stop()
115 timer.stop()
94 except KeyboardInterrupt:
116 except KeyboardInterrupt:
95 ignore_CTRL_C()
117 ignore_CTRL_C()
@@ -41,6 +41,7 b' from IPython.zmq.ipkernel import Kernel, IPKernelApp'
41 from IPython.zmq.session import (
41 from IPython.zmq.session import (
42 Session, session_aliases, session_flags
42 Session, session_aliases, session_flags
43 )
43 )
44 from IPython.zmq.zmqshell import ZMQInteractiveShell
44
45
45 from IPython.config.configurable import Configurable
46 from IPython.config.configurable import Configurable
46
47
@@ -143,7 +144,7 b' class IPEngineApp(BaseParallelApplication):'
143 description = _description
144 description = _description
144 examples = _examples
145 examples = _examples
145 config_file_name = Unicode(default_config_file_name)
146 config_file_name = Unicode(default_config_file_name)
146 classes = List([ProfileDir, Session, EngineFactory, Kernel, MPI])
147 classes = List([ZMQInteractiveShell, ProfileDir, Session, EngineFactory, Kernel, MPI])
147
148
148 startup_script = Unicode(u'', config=True,
149 startup_script = Unicode(u'', config=True,
149 help='specify a script to be run at startup')
150 help='specify a script to be run at startup')
@@ -650,18 +650,27 b' class SSHClusterLauncher(SSHLauncher):'
650
650
651 If not specified, use calling profile, stripping out possible leading homedir.
651 If not specified, use calling profile, stripping out possible leading homedir.
652 """)
652 """)
653
653
654 def _remote_profile_dir_default(self):
654 def _profile_dir_changed(self, name, old, new):
655 """turns /home/you/.ipython/profile_foo into .ipython/profile_foo
655 if not self.remote_profile_dir:
656 """
656 # trigger remote_profile_dir_default logic again,
657 # in case it was already triggered before profile_dir was set
658 self.remote_profile_dir = self._strip_home(new)
659
660 @staticmethod
661 def _strip_home(path):
662 """turns /home/you/.ipython/profile_foo into .ipython/profile_foo"""
657 home = get_home_dir()
663 home = get_home_dir()
658 if not home.endswith('/'):
664 if not home.endswith('/'):
659 home = home+'/'
665 home = home+'/'
660
666
661 if self.profile_dir.startswith(home):
667 if path.startswith(home):
662 return self.profile_dir[len(home):]
668 return path[len(home):]
663 else:
669 else:
664 return self.profile_dir
670 return path
671
672 def _remote_profile_dir_default(self):
673 return self._strip_home(self.profile_dir)
665
674
666 def _cluster_id_changed(self, name, old, new):
675 def _cluster_id_changed(self, name, old, new):
667 if new:
676 if new:
@@ -373,8 +373,11 b' def shellglob(args):'
373
373
374 """
374 """
375 expanded = []
375 expanded = []
376 # Do not unescape backslash in Windows as it is interpreted as
377 # path separator:
378 unescape = unescape_glob if sys.platform != 'win32' else lambda x: x
376 for a in args:
379 for a in args:
377 expanded.extend(glob.glob(a) or [unescape_glob(a)])
380 expanded.extend(glob.glob(a) or [unescape(a)])
378 return expanded
381 return expanded
379
382
380
383
@@ -73,7 +73,7 b" if have_readline and hasattr(_rl, 'rlmain'):"
73 line = lineobj.TextLine(line)
73 line = lineobj.TextLine(line)
74 return _rl.add_history(line)
74 return _rl.add_history(line)
75
75
76 if sys.platform == 'win32' and have_readline:
76 if (sys.platform == 'win32' or sys.platform == 'cli') and have_readline:
77 try:
77 try:
78 _outputfile=_rl.GetOutputFile()
78 _outputfile=_rl.GetOutputFile()
79 except AttributeError:
79 except AttributeError:
@@ -19,6 +19,7 b' import shutil'
19 import sys
19 import sys
20 import tempfile
20 import tempfile
21 from io import StringIO
21 from io import StringIO
22 from contextlib import contextmanager
22
23
23 from os.path import join, abspath, split
24 from os.path import join, abspath, split
24
25
@@ -447,42 +448,73 b' def test_unicode_in_filename():'
447 str(ex)
448 str(ex)
448
449
449
450
450 def test_shellglob():
451 class TestShellGlob(object):
451 """Test glob expansion for %run magic."""
452 filenames_start_with_a = map('a{0}'.format, range(3))
453 filenames_end_with_b = map('{0}b'.format, range(3))
454 filenames = filenames_start_with_a + filenames_end_with_b
455
452
456 with TemporaryDirectory() as td:
453 @classmethod
457 save = os.getcwdu()
454 def setUpClass(cls):
458 try:
455 cls.filenames_start_with_a = map('a{0}'.format, range(3))
459 os.chdir(td)
456 cls.filenames_end_with_b = map('{0}b'.format, range(3))
457 cls.filenames = cls.filenames_start_with_a + cls.filenames_end_with_b
458 cls.tempdir = TemporaryDirectory()
459 td = cls.tempdir.name
460
460
461 with cls.in_tempdir():
461 # Create empty files
462 # Create empty files
462 for fname in filenames:
463 for fname in cls.filenames:
463 open(os.path.join(td, fname), 'w').close()
464 open(os.path.join(td, fname), 'w').close()
464
465
465 def assert_match(patterns, matches):
466 @classmethod
466 # glob returns unordered list. that's why sorted is required.
467 def tearDownClass(cls):
467 nt.assert_equals(sorted(path.shellglob(patterns)),
468 cls.tempdir.cleanup()
468 sorted(matches))
469
469
470 @classmethod
470 assert_match(['*'], filenames)
471 @contextmanager
471 assert_match(['a*'], filenames_start_with_a)
472 def in_tempdir(cls):
472 assert_match(['*c'], ['*c'])
473 save = os.getcwdu()
473 assert_match(['*', 'a*', '*b', '*c'],
474 try:
474 filenames
475 os.chdir(cls.tempdir.name)
475 + filenames_start_with_a
476 yield
476 + filenames_end_with_b
477 + ['*c'])
478
479 assert_match([r'\*'], ['*'])
480 assert_match([r'a\*', 'a*'], ['a*'] + filenames_start_with_a)
481 assert_match(['a[012]'], filenames_start_with_a)
482 assert_match([r'a\[012]'], ['a[012]'])
483 finally:
477 finally:
484 os.chdir(save)
478 os.chdir(save)
485
479
480 def check_match(self, patterns, matches):
481 with self.in_tempdir():
482 # glob returns unordered list. that's why sorted is required.
483 nt.assert_equals(sorted(path.shellglob(patterns)),
484 sorted(matches))
485
486 def common_cases(self):
487 return [
488 (['*'], self.filenames),
489 (['a*'], self.filenames_start_with_a),
490 (['*c'], ['*c']),
491 (['*', 'a*', '*b', '*c'], self.filenames
492 + self.filenames_start_with_a
493 + self.filenames_end_with_b
494 + ['*c']),
495 (['a[012]'], self.filenames_start_with_a),
496 ]
497
498 @skip_win32
499 def test_match_posix(self):
500 for (patterns, matches) in self.common_cases() + [
501 ([r'\*'], ['*']),
502 ([r'a\*', 'a*'], ['a*'] + self.filenames_start_with_a),
503 ([r'a\[012]'], ['a[012]']),
504 ]:
505 yield (self.check_match, patterns, matches)
506
507 @skip_if_not_win32
508 def test_match_windows(self):
509 for (patterns, matches) in self.common_cases() + [
510 # In windows, backslash is interpreted as path
511 # separator. Therefore, you can't escape glob
512 # using it.
513 ([r'a\*', 'a*'], [r'a\*'] + self.filenames_start_with_a),
514 ([r'a\[012]'], [r'a\[012]']),
515 ]:
516 yield (self.check_match, patterns, matches)
517
486
518
487 def test_unescape_glob():
519 def test_unescape_glob():
488 nt.assert_equals(path.unescape_glob(r'\*\[\!\]\?'), '*[!]?')
520 nt.assert_equals(path.unescape_glob(r'\*\[\!\]\?'), '*[!]?')
@@ -1,4 +1,5 b''
1 include README.rst
1 include README.rst
2 include COPYING.txt
2 include ipython.py
3 include ipython.py
3 include setupbase.py
4 include setupbase.py
4 include setupegg.py
5 include setupegg.py
@@ -162,87 +162,7 b''
162 "\n",
162 "\n",
163 "---\n",
163 "---\n",
164 "\n",
164 "\n",
165 "These equation reference examples are adapted from an [example page in the MathJax documentation](http://cdn.mathjax.org/mathjax/latest/test/sample-eqrefs.html). Note that it's okay to reference equations across cells. Click inside this cell to see the source.\n",
165 "Equation numbering and referencing will be available in a future version of IPython."
166 "\n",
167 "## Labeled equations and references\n",
168 "\n",
169 "Here is a labeled equation:\n",
170 "\\begin{equation}\n",
171 "x+1\\over\\sqrt{1-x^2}\\label{ref1}\n",
172 "\\end{equation}\n",
173 "\n",
174 "with a reference to ref1: \\ref{ref1},\n",
175 "and another numbered one with no label:\n",
176 "\\begin{equation}\n",
177 "x+1\\over\\sqrt{1-x^2}\n",
178 "\\end{equation}"
179 ]
180 },
181 {
182 "cell_type": "markdown",
183 "metadata": {},
184 "source": [
185 "## \\nonumber and equation*\n",
186 "\n",
187 "This one uses \\nonumber:\n",
188 "\\begin{equation}\n",
189 "x+1\\over\\sqrt{1-x^2}\\nonumber\n",
190 "\\end{equation}\n",
191 "\n",
192 "Here's one with the equation* environment:\n",
193 "\\begin{equation*}\n",
194 "x+1\\over\\sqrt{1-x^2}\n",
195 "\\end{equation*}"
196 ]
197 },
198 {
199 "cell_type": "markdown",
200 "metadata": {},
201 "source": [
202 "## Forward references\n",
203 "\n",
204 "This is a forward reference [\\ref{ref2}] and another \\eqref{ref2} for the \n",
205 "following equation:\n",
206 "\n",
207 "\\begin{equation}\n",
208 "x+1\\over\\sqrt{1-x^2}\\label{ref2}\n",
209 "\\end{equation}\n",
210 "\n",
211 "More math:\n",
212 "\\begin{equation}\n",
213 "x+1\\over\\sqrt{1-x^2}\n",
214 "\\end{equation}"
215 ]
216 },
217 {
218 "cell_type": "markdown",
219 "metadata": {},
220 "source": [
221 "### References inline and in environments\n",
222 "\n",
223 "Here is a ref inside math: $\\ref{ref2}+1$ and text after it.\n",
224 "\n",
225 "\\begin{align} \n",
226 "x& = y_1-y_2+y_3-y_5+y_8-\\dots \n",
227 "&& \\text{by \\eqref{ref1}}\\\\ \n",
228 "& = y'\\circ y^* && \\text{(by \\eqref{ref3})}\\\\ \n",
229 "& = y(0) y' && \\text {by Axiom 1.} \n",
230 "\\end{align} \n",
231 "\n",
232 "### Missing references\n",
233 "Here's a bad ref [\\ref{ref4}] to a nonexistent label.\n",
234 "\n",
235 "### Numbering align environments\n",
236 "An alignment:\n",
237 "\\begin{align}\n",
238 "a&=b\\label{ref3}\\cr\n",
239 "&=c+d\n",
240 "\\end{align}\n",
241 "and a starred one:\n",
242 "\\begin{align*}\n",
243 "a&=b\\cr\n",
244 "&=c+d\n",
245 "\\end{align*}"
246 ]
166 ]
247 },
167 },
248 {
168 {
@@ -331,14 +251,6 b''
331 "x=4\n",
251 "x=4\n",
332 "$$"
252 "$$"
333 ]
253 ]
334 },
335 {
336 "cell_type": "code",
337 "collapsed": false,
338 "input": [],
339 "language": "python",
340 "metadata": {},
341 "outputs": []
342 }
254 }
343 ],
255 ],
344 "metadata": {}
256 "metadata": {}
@@ -337,7 +337,7 b' this is just a summary:'
337 * **%pdoc <object>**: Print (or run through a pager if too long) the
337 * **%pdoc <object>**: Print (or run through a pager if too long) the
338 docstring for an object. If the given object is a class, it will
338 docstring for an object. If the given object is a class, it will
339 print both the class and the constructor docstrings.
339 print both the class and the constructor docstrings.
340 * **%pdef <object>**: Print the definition header for any callable
340 * **%pdef <object>**: Print the call signature for any callable
341 object. If the object is a class, print the constructor information.
341 object. If the object is a class, print the constructor information.
342 * **%psource <object>**: Print (or run through a pager if too long)
342 * **%psource <object>**: Print (or run through a pager if too long)
343 the source code for an object.
343 the source code for an object.
@@ -6,6 +6,7 b' This should ONLY be run at real release time.'
6 from __future__ import print_function
6 from __future__ import print_function
7
7
8 from toollib import *
8 from toollib import *
9 from gh_api import post_download
9
10
10 # Get main ipython dir, this will raise if it doesn't pass some checks
11 # Get main ipython dir, this will raise if it doesn't pass some checks
11 ipdir = get_ipdir()
12 ipdir = get_ipdir()
@@ -53,6 +54,11 b" sh(sdists + ' upload')"
53 cd(distdir)
54 cd(distdir)
54 print( 'Uploading distribution files...')
55 print( 'Uploading distribution files...')
55
56
57 for fname in os.listdir('.'):
58 print('uploading %s to GitHub' % fname)
59 desc = "IPython %s source distribution" % version
60 post_download("ipython/ipython", fname, description=desc)
61
56 # Make target dir if it doesn't exist
62 # Make target dir if it doesn't exist
57 sh('ssh %s "mkdir -p %s/release/%s" ' % (archive_user, archive_dir, version))
63 sh('ssh %s "mkdir -p %s/release/%s" ' % (archive_user, archive_dir, version))
58 sh('scp * %s' % release_site)
64 sh('scp * %s' % release_site)
@@ -23,20 +23,34 b' except ImportError:'
23
23
24 github = '--github' in sys.argv
24 github = '--github' in sys.argv
25
25
26 cmd_t = "{py} setup.py bdist_wininst --plat-name={plat}"
26 cmd_t = "{py} setup.py bdist_wininst"
27
27
28 pypi = '--pypi' in sys.argv
28 pypi = '--pypi' in sys.argv
29 pypi_cmd_t = "python setup.py upload_wininst -f {fname}"
29 pypi_cmd_t = "python setup.py upload_wininst -f {fname}"
30
30
31 for py in ['python', 'python3']:
31 # Windows Python cannot normally cross-compile,
32 # so you must have 4 Pythons to make 4 installers:
33 # http://docs.python.org/2/distutils/builtdist.html#cross-compiling-on-windows
34
35 pythons = {
36 2: {
37 'win32' : r'C:\\Python27\Python.exe',
38 'win-amd64': r'C:\\Python27_64\Python.exe',
39 },
40 3: {
41 'win32' : r'C:\\Python33\Python.exe',
42 'win-amd64': r'C:\\Python33_64\Python.exe',
43 },
44 }
45
46 for v,plat_py in pythons.items():
32 # deliberately mangle the name,
47 # deliberately mangle the name,
33 # so easy_install doesn't find these and do horrible wrong things
48 # so easy_install doesn't find these and do horrible wrong things
34 v = 3 if py.endswith('3') else 2
35 try:
49 try:
36 shutil.rmtree('build')
50 shutil.rmtree('build')
37 except OSError:
51 except OSError:
38 pass
52 pass
39 for plat in ['win32', 'win-amd64']:
53 for plat,py in plat_py.items():
40 cmd = cmd_t.format(**locals())
54 cmd = cmd_t.format(**locals())
41 sh(cmd)
55 sh(cmd)
42 orig = glob.glob(os.path.join('dist', 'ipython-*.{plat}.exe'.format(**locals())))[0]
56 orig = glob.glob(os.path.join('dist', 'ipython-*.{plat}.exe'.format(**locals())))[0]
General Comments 0
You need to be logged in to leave comments. Login now