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 | 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('p |
|
|
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. |
@@ -23,8 +23,12 b' 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 |
@@ -44,10 +48,11 b' class ExtensionManager(Configurable):' | |||
|
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, |
@@ -63,6 +68,7 b' class ExtensionManager(Configurable):' | |||
|
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( |
@@ -80,26 +86,43 b' class ExtensionManager(Configurable):' | |||
|
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 |
|
|
|
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. |
@@ -111,21 +134,25 b' class ExtensionManager(Configurable):' | |||
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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. |
@@ -629,7 +629,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
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) |
@@ -173,11 +173,17 b' class magic_arguments(ArgDecorator):' | |||
|
173 | 173 | return func |
|
174 | 174 | |
|
175 | 175 | |
|
176 |
class |
|
|
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 |
@@ -187,18 +193,31 b' class argument(ArgDecorator):' | |||
|
187 | 193 | """ |
|
188 | 194 | if group is not None: |
|
189 | 195 | parser = group |
|
190 |
parser. |
|
|
196 | getattr(parser, self._method_name)(*self.args, **self.kwds) | |
|
191 | 197 | return None |
|
192 | 198 | |
|
193 | 199 | |
|
194 |
class argument |
|
|
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. |
@@ -313,7 +313,8 b' Currently the magic system has the following functions:""",' | |||
|
313 | 313 | import IPython.utils.rlineimpl as readline |
|
314 | 314 | |
|
315 | 315 | if not shell.colors_force and \ |
|
316 |
not readline.have_readline and |
|
|
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: |
@@ -563,6 +563,11 b' python-profiler package from non-free.""")' | |||
|
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 |
@@ -59,14 +59,30 b' class ExtensionMagics(Magics):' | |||
|
59 | 59 | """Load an IPython extension by its module name.""" |
|
60 | 60 | if not module_str: |
|
61 | 61 | raise UsageError('Missing module name.') |
|
62 |
re |
|
|
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): |
@@ -16,10 +16,13 b' from __future__ import print_function' | |||
|
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 | |
@@ -27,16 +30,71 b' from IPython.utils import io' | |||
|
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 | |
@@ -52,43 +110,6 b' class HistoryMagics(Magics):' | |||
|
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 | :: |
@@ -100,11 +121,7 b' class HistoryMagics(Magics):' | |||
|
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 |
@@ -116,9 +133,8 b' class HistoryMagics(Magics):' | |||
|
116 | 133 | return "%s/%s" % (session, line) |
|
117 | 134 | |
|
118 | 135 | # Check if output to specific file was requested. |
|
119 | try: | |
|
120 |
|
|
|
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 |
@@ -135,27 +151,29 b' class HistoryMagics(Magics):' | |||
|
135 | 151 | outfile = io_open(outfname, 'w', encoding='utf-8') |
|
136 | 152 | close_at_end = True |
|
137 | 153 | |
|
138 |
print_nums = |
|
|
139 |
get_output = |
|
|
140 |
pyprompts = |
|
|
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: |
|
|
158 |
hist = history_manager.get_range_by_str(args, |
|
|
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 |
@@ -69,7 +69,7 b' class NamespaceMagics(Magics):' | |||
|
69 | 69 | @skip_doctest |
|
70 | 70 | @line_magic |
|
71 | 71 | def pdef(self, parameter_s='', namespaces=None): |
|
72 |
"""Print the |
|
|
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 | |
@@ -98,7 +98,7 b' class NamespaceMagics(Magics):' | |||
|
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 |
@@ -111,7 +111,7 b' class NamespaceMagics(Magics):' | |||
|
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: |
@@ -344,7 +344,7 b' class Inspector:' | |||
|
344 | 344 | self.set_active_scheme(scheme) |
|
345 | 345 | |
|
346 | 346 | def _getdef(self,obj,oname=''): |
|
347 |
"""Return the |
|
|
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.""" |
@@ -373,7 +373,7 b' class Inspector:' | |||
|
373 | 373 | print() |
|
374 | 374 | |
|
375 | 375 | def pdef(self, obj, oname=''): |
|
376 |
"""Print the |
|
|
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 |
@@ -96,13 +96,41 b' def doctest_run_option_parser():' | |||
|
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 |
|
|
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 | """ |
@@ -514,14 +514,8 b' class AutoreloadMagics(Magics):' | |||
|
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) |
@@ -273,11 +273,7 b' class CythonMagics(Magics):' | |||
|
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) |
@@ -362,10 +362,6 b' __doc__ = __doc__.format(' | |||
|
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) |
@@ -588,10 +588,6 b' __doc__ = __doc__.format(' | |||
|
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) |
@@ -209,12 +209,6 b' class StoreMagics(Magics):' | |||
|
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) |
@@ -22,6 +22,7 b' Authors' | |||
|
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 |
@@ -331,9 +332,10 b' def _paramiko_tunnel(lport, rport, server, remoteip, keyfile=None, password=None' | |||
|
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: |
@@ -470,11 +470,14 b' class NotebookApp(BaseIPythonApplication):' | |||
|
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 |
|
|
|
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: |
@@ -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 | 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; |
@@ -123,6 +123,7 b' var IPython = (function (IPython) {' | |||
|
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 |
@@ -64,7 +64,7 b' var IPython = (function (IPython) {' | |||
|
64 | 64 | |
|
65 | 65 | |
|
66 | 66 | Kernel.prototype.restart = function () { |
|
67 |
$([IPython.events]).trigger( |
|
|
67 | $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this}); | |
|
68 | 68 | var that = this; |
|
69 | 69 | if (this.running) { |
|
70 | 70 | this.stop_channels(); |
@@ -86,6 +86,7 b' var IPython = (function (IPython) {' | |||
|
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 | |
@@ -245,7 +246,8 b' var IPython = (function (IPython) {' | |||
|
245 | 246 | user_expressions : {}, |
|
246 | 247 | allow_stdin : false |
|
247 | 248 | }; |
|
248 |
|
|
|
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); |
@@ -279,7 +281,7 b' var IPython = (function (IPython) {' | |||
|
279 | 281 | |
|
280 | 282 | Kernel.prototype.interrupt = function () { |
|
281 | 283 | if (this.running) { |
|
282 |
$([IPython.events]).trigger( |
|
|
284 | $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this}); | |
|
283 | 285 | $.post(this.kernel_url + "/interrupt"); |
|
284 | 286 | }; |
|
285 | 287 | }; |
@@ -312,6 +314,7 b' var IPython = (function (IPython) {' | |||
|
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; |
@@ -367,12 +370,12 b' var IPython = (function (IPython) {' | |||
|
367 | 370 | } |
|
368 | 371 | } else if (msg_type === 'status') { |
|
369 | 372 | if (content.execution_state === 'busy') { |
|
370 |
$([IPython.events]).trigger( |
|
|
373 | $([IPython.events]).trigger('status_busy.Kernel', {kernel: this}); | |
|
371 | 374 | } else if (content.execution_state === 'idle') { |
|
372 |
$([IPython.events]).trigger( |
|
|
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( |
|
|
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']; |
@@ -51,10 +51,10 b' var IPython = (function (IPython) {' | |||
|
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'); |
@@ -14,10 +14,9 b" IPython.namespace('IPython.mathjaxutils');" | |||
|
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: [ ['$$','$$'], ["\\[","\\]"] ], |
@@ -28,6 +27,7 b' IPython.mathjaxutils = (function (IPython) {' | |||
|
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>') |
@@ -88,7 +88,6 b' IPython.mathjaxutils = (function (IPython) {' | |||
|
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. |
@@ -102,11 +101,12 b' IPython.mathjaxutils = (function (IPython) {' | |||
|
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, "&") // use HTML entity for & |
|
106 | 106 | .replace(/</g, "<") // use HTML entity for < |
|
107 | 107 | .replace(/>/g, ">") // use HTML entity for > |
|
108 | 108 | ; |
|
109 |
if ( |
|
|
109 | if (hub.Browser.isMSIE) { | |
|
110 | 110 | block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n") |
|
111 | 111 | } |
|
112 | 112 | while (j > i) { |
@@ -127,6 +127,10 b' IPython.mathjaxutils = (function (IPython) {' | |||
|
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 | |
@@ -216,6 +220,10 b' IPython.mathjaxutils = (function (IPython) {' | |||
|
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 | }); |
@@ -223,21 +231,11 b' IPython.mathjaxutils = (function (IPython) {' | |||
|
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 |
@@ -128,7 +128,13 b' var IPython = (function (IPython) {' | |||
|
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 | }); |
@@ -22,6 +22,9 b' var IPython = (function (IPython) {' | |||
|
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 = {}; |
@@ -139,8 +142,8 b' var IPython = (function (IPython) {' | |||
|
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) { |
@@ -257,6 +260,11 b' var IPython = (function (IPython) {' | |||
|
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; |
@@ -536,13 +544,19 b' var IPython = (function (IPython) {' | |||
|
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 | }; |
@@ -560,6 +574,11 b' var IPython = (function (IPython) {' | |||
|
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); |
@@ -594,6 +613,9 b' var IPython = (function (IPython) {' | |||
|
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); |
@@ -756,8 +778,8 b' var IPython = (function (IPython) {' | |||
|
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') |
@@ -769,7 +791,7 b' var IPython = (function (IPython) {' | |||
|
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; |
@@ -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 | 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); |
@@ -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 | 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 | 1110 | Notebook.prototype.execute_all_cells = function () { |
|
1053 |
|
|
|
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 |
@@ -379,8 +379,10 b' var IPython = (function (IPython) {' | |||
|
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 | } |
@@ -33,6 +33,7 b' var IPython = (function (IPython) {' | |||
|
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'}, |
@@ -221,11 +221,9 b' var IPython = (function (IPython) {' | |||
|
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) { |
@@ -235,7 +233,6 b' var IPython = (function (IPython) {' | |||
|
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"); |
@@ -250,8 +247,7 b' var IPython = (function (IPython) {' | |||
|
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 | }; |
@@ -195,11 +195,30 b' IPython.utils = (function (IPython) {' | |||
|
195 | 195 | tmp = txt; |
|
196 | 196 | do { |
|
197 | 197 | txt = tmp; |
|
198 |
tmp = txt.replace(/ |
|
|
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. |
@@ -261,6 +280,8 b' IPython.utils = (function (IPython) {' | |||
|
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 |
@@ -2,7 +2,7 b'' | |||
|
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, |
@@ -75,9 +75,9 b' data-notebook-id={{notebook_id}}' | |||
|
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 & 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> |
@@ -108,6 +108,8 b' data-notebook-id={{notebook_id}}' | |||
|
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> |
@@ -3,7 +3,7 b'' | |||
|
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, |
@@ -268,7 +268,6 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||
|
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 = '' |
@@ -343,7 +342,51 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||
|
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 |
@@ -392,39 +435,6 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||
|
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 |
@@ -1035,8 +1045,12 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||
|
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 |
|
|
1053 | # mouse events. | |
|
1040 | 1054 | control.installEventFilter(self) |
|
1041 | 1055 | control.viewport().installEventFilter(self) |
|
1042 | 1056 | |
@@ -1773,6 +1787,32 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||
|
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. |
@@ -528,7 +528,25 b' class MainWindow(QtGui.QMainWindow):' | |||
|
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 |
@@ -829,6 +847,9 b' class MainWindow(QtGui.QMainWindow):' | |||
|
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 |
@@ -1,7 +1,8 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 | |
@@ -17,24 +18,40 b' class YouTubeVideo(object):' | |||
|
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 |
@@ -240,4 +257,4 b' class FileLinks(FileLink):' | |||
|
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) |
@@ -85,11 +85,33 b' def create_inputhook_qt4(mgr, app=None):' | |||
|
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 |
|
|
|
114 | event_loop.exec_() | |
|
93 | 115 | timer.stop() |
|
94 | 116 | except KeyboardInterrupt: |
|
95 | 117 | ignore_CTRL_C() |
@@ -41,6 +41,7 b' 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 | |
@@ -143,7 +144,7 b' class IPEngineApp(BaseParallelApplication):' | |||
|
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') |
@@ -650,18 +650,27 b' class SSHClusterLauncher(SSHLauncher):' | |||
|
650 | 650 | |
|
651 | 651 | If not specified, use calling profile, stripping out possible leading homedir. |
|
652 | 652 | """) |
|
653 | ||
|
654 |
def |
|
|
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 |
|
|
662 |
return |
|
|
667 | if path.startswith(home): | |
|
668 | return path[len(home):] | |
|
663 | 669 | else: |
|
664 |
return |
|
|
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: |
@@ -373,8 +373,11 b' def shellglob(args):' | |||
|
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 |
|
|
380 | expanded.extend(glob.glob(a) or [unescape(a)]) | |
|
378 | 381 | return expanded |
|
379 | 382 | |
|
380 | 383 |
@@ -73,7 +73,7 b" if have_readline and hasattr(_rl, 'rlmain'):" | |||
|
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: |
@@ -19,6 +19,7 b' 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 | |
@@ -447,42 +448,73 b' def test_unicode_in_filename():' | |||
|
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'\*\[\!\]\?'), '*[!]?') |
@@ -1,4 +1,5 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 |
@@ -162,87 +162,7 b'' | |||
|
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 | { |
@@ -331,14 +251,6 b'' | |||
|
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": {} |
@@ -337,7 +337,7 b' this is just a summary:' | |||
|
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 |
|
|
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. |
@@ -6,6 +6,7 b' This should ONLY be run at real release time.' | |||
|
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() |
@@ -53,6 +54,11 b" 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) |
@@ -23,20 +23,34 b' except ImportError:' | |||
|
23 | 23 | |
|
24 | 24 | github = '--github' in sys.argv |
|
25 | 25 | |
|
26 |
cmd_t = "{py} setup.py bdist_wininst |
|
|
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] |
General Comments 0
You need to be logged in to leave comments.
Login now