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") |
@@ -0,0 +1,33 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2012 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
7 | ||
|
8 | //============================================================================ | |
|
9 | // Notebook | |
|
10 | //============================================================================ | |
|
11 | ||
|
12 | var IPython = (function (IPython) { | |
|
13 | ||
|
14 | var config = { | |
|
15 | cell_magic_highlight : { | |
|
16 | 'magic_javascript':{'reg':[/^%%javascript/]} | |
|
17 | ,'magic_perl' :{'reg':[/^%%perl/]} | |
|
18 | ,'magic_ruby' :{'reg':[/^%%ruby/]} | |
|
19 | ,'magic_python' :{'reg':[/^%%python3?/]} | |
|
20 | ,'magic_shell' :{'reg':[/^%%bash/]} | |
|
21 | ,'magic_r' :{'reg':[/^%%R/]} | |
|
22 | }, | |
|
23 | raw_cell_highlight : { | |
|
24 | 'diff' :{'reg':[/^diff/]} | |
|
25 | } | |
|
26 | }; | |
|
27 | ||
|
28 | IPython.config = config; | |
|
29 | ||
|
30 | return IPython; | |
|
31 | ||
|
32 | }(IPython)); | |
|
33 |
@@ -0,0 +1,179 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2011 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
7 | ||
|
8 | //============================================================================ | |
|
9 | // ToolBar | |
|
10 | //============================================================================ | |
|
11 | ||
|
12 | var IPython = (function (IPython) { | |
|
13 | ||
|
14 | var MainToolBar = function (selector) { | |
|
15 | this.selector = selector; | |
|
16 | IPython.ToolBar.apply(this, arguments); | |
|
17 | this.construct(); | |
|
18 | this.add_drop_down_list(); | |
|
19 | this.bind_events(); | |
|
20 | }; | |
|
21 | ||
|
22 | MainToolBar.prototype = new IPython.ToolBar(); | |
|
23 | ||
|
24 | MainToolBar.prototype.construct = function () { | |
|
25 | this.add_buttons_group([ | |
|
26 | { | |
|
27 | id : 'save_b', | |
|
28 | label : 'Save', | |
|
29 | icon : 'ui-icon-disk', | |
|
30 | callback : function () { | |
|
31 | IPython.notebook.save_notebook(); | |
|
32 | } | |
|
33 | } | |
|
34 | ]); | |
|
35 | this.add_buttons_group([ | |
|
36 | { | |
|
37 | id : 'cut_b', | |
|
38 | label : 'Cut Cell', | |
|
39 | icon : 'ui-icon-scissors', | |
|
40 | callback : function () { | |
|
41 | IPython.notebook.cut_cell(); | |
|
42 | } | |
|
43 | }, | |
|
44 | { | |
|
45 | id : 'copy_b', | |
|
46 | label : 'Copy Cell', | |
|
47 | icon : 'ui-icon-copy', | |
|
48 | callback : function () { | |
|
49 | IPython.notebook.copy_cell(); | |
|
50 | } | |
|
51 | }, | |
|
52 | { | |
|
53 | id : 'paste_b', | |
|
54 | label : 'Paste Cell Below', | |
|
55 | icon : 'ui-icon-clipboard', | |
|
56 | callback : function () { | |
|
57 | IPython.notebook.paste_cell_below(); | |
|
58 | } | |
|
59 | } | |
|
60 | ],'cut_copy_paste'); | |
|
61 | ||
|
62 | this.add_buttons_group([ | |
|
63 | { | |
|
64 | id : 'move_up_b', | |
|
65 | label : 'Move Cell Up', | |
|
66 | icon : 'ui-icon-arrowthick-1-n', | |
|
67 | callback : function () { | |
|
68 | IPython.notebook.move_cell_up(); | |
|
69 | } | |
|
70 | }, | |
|
71 | { | |
|
72 | id : 'move_down_b', | |
|
73 | label : 'Move Cell Down', | |
|
74 | icon : 'ui-icon-arrowthick-1-s', | |
|
75 | callback : function () { | |
|
76 | IPython.notebook.move_cell_down(); | |
|
77 | } | |
|
78 | } | |
|
79 | ],'move_up_down'); | |
|
80 | ||
|
81 | this.add_buttons_group([ | |
|
82 | { | |
|
83 | id : 'insert_above_b', | |
|
84 | label : 'Insert Cell Above', | |
|
85 | icon : 'ui-icon-arrowthickstop-1-n', | |
|
86 | callback : function () { | |
|
87 | IPython.notebook.insert_cell_above('code'); | |
|
88 | } | |
|
89 | }, | |
|
90 | { | |
|
91 | id : 'insert_below_b', | |
|
92 | label : 'Insert Cell Below', | |
|
93 | icon : 'ui-icon-arrowthickstop-1-s', | |
|
94 | callback : function () { | |
|
95 | IPython.notebook.insert_cell_below('code'); | |
|
96 | } | |
|
97 | } | |
|
98 | ],'insert_above_below'); | |
|
99 | ||
|
100 | this.add_buttons_group([ | |
|
101 | { | |
|
102 | id : 'run_b', | |
|
103 | label : 'Run Cell', | |
|
104 | icon : 'ui-icon-play', | |
|
105 | callback : function () { | |
|
106 | IPython.notebook.execute_selected_cell(); | |
|
107 | } | |
|
108 | }, | |
|
109 | { | |
|
110 | id : 'interrupt_b', | |
|
111 | label : 'Interrupt', | |
|
112 | icon : 'ui-icon-stop', | |
|
113 | callback : function () { | |
|
114 | IPython.notebook.kernel.interrupt(); | |
|
115 | } | |
|
116 | } | |
|
117 | ],'run_int'); | |
|
118 | ||
|
119 | ||
|
120 | }; | |
|
121 | ||
|
122 | MainToolBar.prototype.add_drop_down_list = function () { | |
|
123 | var select = $(this.selector) | |
|
124 | .append($('<select/>') | |
|
125 | .attr('id','cell_type') | |
|
126 | .addClass('ui-widget ui-widget-content') | |
|
127 | .append($('<option/>').attr('value','code').text('Code')) | |
|
128 | .append($('<option/>').attr('value','markdown').text('Markdown')) | |
|
129 | .append($('<option/>').attr('value','raw').text('Raw Text')) | |
|
130 | .append($('<option/>').attr('value','heading1').text('Heading 1')) | |
|
131 | .append($('<option/>').attr('value','heading2').text('Heading 2')) | |
|
132 | .append($('<option/>').attr('value','heading3').text('Heading 3')) | |
|
133 | .append($('<option/>').attr('value','heading4').text('Heading 4')) | |
|
134 | .append($('<option/>').attr('value','heading5').text('Heading 5')) | |
|
135 | .append($('<option/>').attr('value','heading6').text('Heading 6')) | |
|
136 | .append($('<option/>').attr('value','heading7').text('Heading 7')) | |
|
137 | .append($('<option/>').attr('value','heading8').text('Heading 8')) | |
|
138 | ); | |
|
139 | }; | |
|
140 | ||
|
141 | MainToolBar.prototype.bind_events = function () { | |
|
142 | var that = this; | |
|
143 | ||
|
144 | this.element.find('#cell_type').change(function () { | |
|
145 | var cell_type = $(this).val(); | |
|
146 | if (cell_type === 'code') { | |
|
147 | IPython.notebook.to_code(); | |
|
148 | } else if (cell_type === 'markdown') { | |
|
149 | IPython.notebook.to_markdown(); | |
|
150 | } else if (cell_type === 'raw') { | |
|
151 | IPython.notebook.to_raw(); | |
|
152 | } else if (cell_type === 'heading1') { | |
|
153 | IPython.notebook.to_heading(undefined, 1); | |
|
154 | } else if (cell_type === 'heading2') { | |
|
155 | IPython.notebook.to_heading(undefined, 2); | |
|
156 | } else if (cell_type === 'heading3') { | |
|
157 | IPython.notebook.to_heading(undefined, 3); | |
|
158 | } else if (cell_type === 'heading4') { | |
|
159 | IPython.notebook.to_heading(undefined, 4); | |
|
160 | } else if (cell_type === 'heading5') { | |
|
161 | IPython.notebook.to_heading(undefined, 5); | |
|
162 | } else if (cell_type === 'heading6') { | |
|
163 | IPython.notebook.to_heading(undefined, 6); | |
|
164 | } | |
|
165 | }); | |
|
166 | $([IPython.events]).on('selected_cell_type_changed.Notebook', function (event, data) { | |
|
167 | if (data.cell_type === 'heading') { | |
|
168 | that.element.find('#cell_type').val(data.cell_type+data.level); | |
|
169 | } else { | |
|
170 | that.element.find('#cell_type').val(data.cell_type); | |
|
171 | } | |
|
172 | }); | |
|
173 | }; | |
|
174 | ||
|
175 | IPython.MainToolBar = MainToolBar; | |
|
176 | ||
|
177 | return IPython; | |
|
178 | ||
|
179 | }(IPython)); |
@@ -0,0 +1,95 b'' | |||
|
1 | #----------------------------------------------------------------------------- | |
|
2 | # Copyright (C) 2012 The IPython Development Team | |
|
3 | # | |
|
4 | # Distributed under the terms of the BSD License. The full license is in | |
|
5 | # the file COPYING, distributed as part of this software. | |
|
6 | #----------------------------------------------------------------------------- | |
|
7 | ||
|
8 | import os | |
|
9 | import sys | |
|
10 | import unittest | |
|
11 | import base64 | |
|
12 | ||
|
13 | from IPython.zmq.kernelmanager import KernelManager | |
|
14 | from IPython.frontend.terminal.console.interactiveshell \ | |
|
15 | import ZMQTerminalInteractiveShell | |
|
16 | from IPython.utils.tempdir import TemporaryDirectory | |
|
17 | from IPython.testing.tools import monkeypatch | |
|
18 | from IPython.testing.decorators import skip_without | |
|
19 | from IPython.utils.ipstruct import Struct | |
|
20 | ||
|
21 | ||
|
22 | SCRIPT_PATH = os.path.join( | |
|
23 | os.path.abspath(os.path.dirname(__file__)), 'writetofile.py') | |
|
24 | ||
|
25 | ||
|
26 | class ZMQTerminalInteractiveShellTestCase(unittest.TestCase): | |
|
27 | ||
|
28 | def setUp(self): | |
|
29 | km = KernelManager() | |
|
30 | self.shell = ZMQTerminalInteractiveShell(kernel_manager=km) | |
|
31 | self.raw = b'dummy data' | |
|
32 | self.mime = 'image/png' | |
|
33 | self.data = {self.mime: base64.encodestring(self.raw).decode('ascii')} | |
|
34 | ||
|
35 | def test_no_call_by_default(self): | |
|
36 | def raise_if_called(*args, **kwds): | |
|
37 | assert False | |
|
38 | ||
|
39 | shell = self.shell | |
|
40 | shell.handle_image_PIL = raise_if_called | |
|
41 | shell.handle_image_stream = raise_if_called | |
|
42 | shell.handle_image_tempfile = raise_if_called | |
|
43 | shell.handle_image_callable = raise_if_called | |
|
44 | ||
|
45 | shell.handle_image(None, None) # arguments are dummy | |
|
46 | ||
|
47 | @skip_without('PIL') | |
|
48 | def test_handle_image_PIL(self): | |
|
49 | import PIL.Image | |
|
50 | ||
|
51 | open_called_with = [] | |
|
52 | show_called_with = [] | |
|
53 | ||
|
54 | def fake_open(arg): | |
|
55 | open_called_with.append(arg) | |
|
56 | return Struct(show=lambda: show_called_with.append(None)) | |
|
57 | ||
|
58 | with monkeypatch(PIL.Image, 'open', fake_open): | |
|
59 | self.shell.handle_image_PIL(self.data, self.mime) | |
|
60 | ||
|
61 | self.assertEqual(len(open_called_with), 1) | |
|
62 | self.assertEqual(len(show_called_with), 1) | |
|
63 | self.assertEqual(open_called_with[0].getvalue(), self.raw) | |
|
64 | ||
|
65 | def check_handler_with_file(self, inpath, handler): | |
|
66 | shell = self.shell | |
|
67 | configname = '{0}_image_handler'.format(handler) | |
|
68 | funcname = 'handle_image_{0}'.format(handler) | |
|
69 | ||
|
70 | assert hasattr(shell, configname) | |
|
71 | assert hasattr(shell, funcname) | |
|
72 | ||
|
73 | with TemporaryDirectory() as tmpdir: | |
|
74 | outpath = os.path.join(tmpdir, 'data') | |
|
75 | cmd = [sys.executable, SCRIPT_PATH, inpath, outpath] | |
|
76 | setattr(shell, configname, cmd) | |
|
77 | getattr(shell, funcname)(self.data, self.mime) | |
|
78 | # cmd is called and file is closed. So it's safe to open now. | |
|
79 | with open(outpath, 'rb') as file: | |
|
80 | transferred = file.read() | |
|
81 | ||
|
82 | self.assertEqual(transferred, self.raw) | |
|
83 | ||
|
84 | def test_handle_image_stream(self): | |
|
85 | self.check_handler_with_file('-', 'stream') | |
|
86 | ||
|
87 | def test_handle_image_tempfile(self): | |
|
88 | self.check_handler_with_file('{file}', 'tempfile') | |
|
89 | ||
|
90 | def test_handle_image_callable(self): | |
|
91 | called_with = [] | |
|
92 | self.shell.callable_image_handler = called_with.append | |
|
93 | self.shell.handle_image_callable(self.data, self.mime) | |
|
94 | self.assertEqual(len(called_with), 1) | |
|
95 | assert called_with[0] is self.data |
@@ -0,0 +1,33 b'' | |||
|
1 | #----------------------------------------------------------------------------- | |
|
2 | # Copyright (C) 2012 The IPython Development Team | |
|
3 | # | |
|
4 | # Distributed under the terms of the BSD License. The full license is in | |
|
5 | # the file COPYING, distributed as part of this software. | |
|
6 | #----------------------------------------------------------------------------- | |
|
7 | ||
|
8 | """ | |
|
9 | Copy data from input file to output file for testing. | |
|
10 | ||
|
11 | Command line usage: | |
|
12 | ||
|
13 | python writetofile.py INPUT OUTPUT | |
|
14 | ||
|
15 | Binary data from INPUT file is copied to OUTPUT file. | |
|
16 | If INPUT is '-', stdin is used. | |
|
17 | ||
|
18 | """ | |
|
19 | ||
|
20 | if __name__ == '__main__': | |
|
21 | import sys | |
|
22 | from IPython.utils.py3compat import PY3 | |
|
23 | (inpath, outpath) = sys.argv[1:] | |
|
24 | ||
|
25 | if inpath == '-': | |
|
26 | if PY3: | |
|
27 | infile = sys.stdin.buffer | |
|
28 | else: | |
|
29 | infile = sys.stdin | |
|
30 | else: | |
|
31 | infile = open(inpath, 'rb') | |
|
32 | ||
|
33 | open(outpath, 'w+b').write(infile.read()) |
@@ -0,0 +1,20 b'' | |||
|
1 | #----------------------------------------------------------------------------- | |
|
2 | # Copyright (C) 2012- The IPython Development Team | |
|
3 | # | |
|
4 | # Distributed under the terms of the BSD License. The full license is in | |
|
5 | # the file COPYING, distributed as part of this software. | |
|
6 | #----------------------------------------------------------------------------- | |
|
7 | ||
|
8 | import os | |
|
9 | ||
|
10 | from IPython.utils.tempdir import NamedFileInTemporaryDirectory | |
|
11 | ||
|
12 | ||
|
13 | def test_named_file_in_temporary_directory(): | |
|
14 | with NamedFileInTemporaryDirectory('filename') as file: | |
|
15 | name = file.name | |
|
16 | assert not file.closed | |
|
17 | assert os.path.exists(name) | |
|
18 | file.write(b'test') | |
|
19 | assert file.closed | |
|
20 | assert not os.path.exists(name) |
@@ -0,0 +1,45 b'' | |||
|
1 | """Wrapper around linecache which decodes files to unicode according to PEP 263. | |
|
2 | ||
|
3 | This is only needed for Python 2 - linecache in Python 3 does the same thing | |
|
4 | itself. | |
|
5 | """ | |
|
6 | import functools | |
|
7 | import linecache | |
|
8 | import sys | |
|
9 | ||
|
10 | from IPython.utils import py3compat | |
|
11 | from IPython.utils import openpy | |
|
12 | ||
|
13 | if py3compat.PY3: | |
|
14 | getline = linecache.getline | |
|
15 | ||
|
16 | # getlines has to be looked up at runtime, because doctests monkeypatch it. | |
|
17 | @functools.wraps(linecache.getlines) | |
|
18 | def getlines(filename, module_globals=None): | |
|
19 | return linecache.getlines(filename, module_globals=module_globals) | |
|
20 | ||
|
21 | else: | |
|
22 | def getlines(filename, module_globals=None): | |
|
23 | """Get the lines (as unicode) for a file from the cache. | |
|
24 | Update the cache if it doesn't contain an entry for this file already.""" | |
|
25 | filename = py3compat.cast_bytes(filename, sys.getfilesystemencoding()) | |
|
26 | lines = linecache.getlines(filename, module_globals=module_globals) | |
|
27 | ||
|
28 | # The bits we cache ourselves can be unicode. | |
|
29 | if (not lines) or isinstance(lines[0], unicode): | |
|
30 | return lines | |
|
31 | ||
|
32 | readline = openpy._list_readline(lines) | |
|
33 | try: | |
|
34 | encoding, _ = openpy.detect_encoding(readline) | |
|
35 | except SyntaxError: | |
|
36 | encoding = 'ascii' | |
|
37 | return [l.decode(encoding, 'replace') for l in lines] | |
|
38 | ||
|
39 | # This is a straight copy of linecache.getline | |
|
40 | def getline(filename, lineno, module_globals=None): | |
|
41 | lines = getlines(filename, module_globals) | |
|
42 | if 1 <= lineno <= len(lines): | |
|
43 | return lines[lineno-1] | |
|
44 | else: | |
|
45 | return '' |
@@ -0,0 +1,106 b'' | |||
|
1 | # load with: . ipython-completion.bash | |
|
2 | ||
|
3 | _ipython_get_flags() | |
|
4 | { | |
|
5 | local url=$1 | |
|
6 | local var=$2 | |
|
7 | local dash=$3 | |
|
8 | if [[ "$url $var" == $__ipython_complete_last ]]; then | |
|
9 | opts=$__ipython_complete_last_res | |
|
10 | return | |
|
11 | fi | |
|
12 | # pylab and profile don't need the = and the | |
|
13 | # version without simplifies the special cased completion | |
|
14 | opts=$(ipython ${url} --help-all | grep -E "^-{1,2}[^-]" | sed -e "s/<.*//" -e "s/[^=]$/& /" -e "s/^--pylab=$//" -e "s/^--profile=$/--profile /") | |
|
15 | __ipython_complete_last="$url $var" | |
|
16 | __ipython_complete_last_res="$opts" | |
|
17 | } | |
|
18 | ||
|
19 | _ipython() | |
|
20 | { | |
|
21 | local cur=${COMP_WORDS[COMP_CWORD]} | |
|
22 | local prev=${COMP_WORDS[COMP_CWORD - 1]} | |
|
23 | local subcommands="notebook qtconsole console kernel profile locate" | |
|
24 | local opts="" | |
|
25 | if [ -z "$__ipython_complete_baseopts" ]; then | |
|
26 | _ipython_get_flags baseopts | |
|
27 | __ipython_complete_baseopts="${opts}" | |
|
28 | fi | |
|
29 | local baseopts="$__ipython_complete_baseopts" | |
|
30 | local mode="" | |
|
31 | for i in "${COMP_WORDS[@]}"; do | |
|
32 | [ "$cur" = "$i" ] && break | |
|
33 | if [[ ${subcommands} == *${i}* ]]; then | |
|
34 | mode="$i" | |
|
35 | break | |
|
36 | elif [[ ${i} == "--"* ]]; then | |
|
37 | mode="nosubcommand" | |
|
38 | break | |
|
39 | fi | |
|
40 | done | |
|
41 | ||
|
42 | if [[ $mode == "profile" ]]; then | |
|
43 | opts="list create" | |
|
44 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) | |
|
45 | elif [[ ${cur} == -* ]]; then | |
|
46 | if [[ $mode == "notebook" ]]; then | |
|
47 | _ipython_get_flags notebook | |
|
48 | opts=$"${opts} ${baseopts}" | |
|
49 | elif [[ $mode == "qtconsole" ]]; then | |
|
50 | _ipython_get_flags qtconsole | |
|
51 | opts="${opts} ${baseopts}" | |
|
52 | elif [[ $mode == "console" ]]; then | |
|
53 | _ipython_get_flags console | |
|
54 | elif [[ $mode == "kernel" ]]; then | |
|
55 | _ipython_get_flags kernel | |
|
56 | opts="${opts} ${baseopts}" | |
|
57 | elif [[ $mode == "locate" ]]; then | |
|
58 | opts="" | |
|
59 | else | |
|
60 | opts=$baseopts | |
|
61 | fi | |
|
62 | # don't drop the trailing space | |
|
63 | local IFS=$'\t\n' | |
|
64 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) | |
|
65 | return 0 | |
|
66 | elif [[ ${prev} == "--pylab"* ]] || [[ ${prev} == "--gui"* ]]; then | |
|
67 | if [ -z "$__ipython_complete_pylab" ]; then | |
|
68 | __ipython_complete_pylab=`cat <<EOF | python - | |
|
69 | try: | |
|
70 | import IPython.core.shellapp as mod; | |
|
71 | for k in mod.InteractiveShellApp.pylab.values: | |
|
72 | print "%s " % k | |
|
73 | except: | |
|
74 | pass | |
|
75 | EOF | |
|
76 | ` | |
|
77 | fi | |
|
78 | local IFS=$'\t\n' | |
|
79 | COMPREPLY=( $(compgen -W "${__ipython_complete_pylab}" -- ${cur}) ) | |
|
80 | elif [[ ${prev} == "--profile"* ]]; then | |
|
81 | if [ -z "$__ipython_complete_profiles" ]; then | |
|
82 | __ipython_complete_profiles=`cat <<EOF | python - | |
|
83 | try: | |
|
84 | import IPython.core.profileapp | |
|
85 | for k in IPython.core.profileapp.list_bundled_profiles(): | |
|
86 | print "%s " % k | |
|
87 | p = IPython.core.profileapp.ProfileList() | |
|
88 | for k in IPython.core.profileapp.list_profiles_in(p.ipython_dir): | |
|
89 | print "%s " % k | |
|
90 | except: | |
|
91 | pass | |
|
92 | EOF | |
|
93 | ` | |
|
94 | fi | |
|
95 | local IFS=$'\t\n' | |
|
96 | COMPREPLY=( $(compgen -W "${__ipython_complete_profiles}" -- ${cur}) ) | |
|
97 | else | |
|
98 | if [ -z "$mode" ]; then | |
|
99 | COMPREPLY=( $(compgen -f -W "${subcommands}" -- ${cur}) ) | |
|
100 | else | |
|
101 | COMPREPLY=( $(compgen -f -- ${cur}) ) | |
|
102 | fi | |
|
103 | fi | |
|
104 | ||
|
105 | } | |
|
106 | complete -o default -o nospace -F _ipython ipython |
@@ -0,0 +1,259 b'' | |||
|
1 | { | |
|
2 | "metadata": { | |
|
3 | "name": "Typesetting Math Using MathJax" | |
|
4 | }, | |
|
5 | "nbformat": 3, | |
|
6 | "nbformat_minor": 0, | |
|
7 | "worksheets": [ | |
|
8 | { | |
|
9 | "cells": [ | |
|
10 | { | |
|
11 | "cell_type": "markdown", | |
|
12 | "metadata": {}, | |
|
13 | "source": [ | |
|
14 | "The Markdown parser included in IPython is MathJax-aware. This means that you can freely mix in mathematical expressions using the [MathJax subset of Tex and LaTeX](http://docs.mathjax.org/en/latest/tex.html#tex-support). [Some examples from the MathJax site](http://www.mathjax.org/demos/tex-samples/) are reproduced below, as well as the Markdown+TeX source." | |
|
15 | ] | |
|
16 | }, | |
|
17 | { | |
|
18 | "cell_type": "markdown", | |
|
19 | "metadata": {}, | |
|
20 | "source": [ | |
|
21 | "# Motivating Examples\n", | |
|
22 | "\n", | |
|
23 | "---\n", | |
|
24 | "\n", | |
|
25 | "## The Lorenz Equations\n", | |
|
26 | "### Source\n", | |
|
27 | "```\\begin{aligned}\n", | |
|
28 | "\\dot{x} & = \\sigma(y-x) \\\\\n", | |
|
29 | "\\dot{y} & = \\rho x - y - xz \\\\\n", | |
|
30 | "\\dot{z} & = -\\beta z + xy\n", | |
|
31 | "\\end{aligned}\n", | |
|
32 | "```\n", | |
|
33 | "### Display\n", | |
|
34 | "\\begin{aligned}\n", | |
|
35 | "\\dot{x} & = \\sigma(y-x) \\\\\n", | |
|
36 | "\\dot{y} & = \\rho x - y - xz \\\\\n", | |
|
37 | "\\dot{z} & = -\\beta z + xy\n", | |
|
38 | "\\end{aligned}" | |
|
39 | ] | |
|
40 | }, | |
|
41 | { | |
|
42 | "cell_type": "markdown", | |
|
43 | "metadata": {}, | |
|
44 | "source": [ | |
|
45 | "## The Cauchy-Schwarz Inequality\n", | |
|
46 | "### Source\n", | |
|
47 | "```\\begin{equation*}\n", | |
|
48 | "\\left( \\sum_{k=1}^n a_k b_k \\right)^2 \\leq \\left( \\sum_{k=1}^n a_k^2 \\right) \\left( \\sum_{k=1}^n b_k^2 \\right)\n", | |
|
49 | "\\end{equation*}\n", | |
|
50 | "```\n", | |
|
51 | "### Display\n", | |
|
52 | "\\begin{equation*}\n", | |
|
53 | "\\left( \\sum_{k=1}^n a_k b_k \\right)^2 \\leq \\left( \\sum_{k=1}^n a_k^2 \\right) \\left( \\sum_{k=1}^n b_k^2 \\right)\n", | |
|
54 | "\\end{equation*}" | |
|
55 | ] | |
|
56 | }, | |
|
57 | { | |
|
58 | "cell_type": "markdown", | |
|
59 | "metadata": {}, | |
|
60 | "source": [ | |
|
61 | "## A Cross Product Formula\n", | |
|
62 | "### Source\n", | |
|
63 | "```\\begin{equation*}\n", | |
|
64 | "\\mathbf{V}_1 \\times \\mathbf{V}_2 = \\begin{vmatrix}\n", | |
|
65 | "\\mathbf{i} & \\mathbf{j} & \\mathbf{k} \\\\\n", | |
|
66 | "\\frac{\\partial X}{\\partial u} & \\frac{\\partial Y}{\\partial u} & 0 \\\\\n", | |
|
67 | "\\frac{\\partial X}{\\partial v} & \\frac{\\partial Y}{\\partial v} & 0\n", | |
|
68 | "\\end{vmatrix} \n", | |
|
69 | "\\end{equation*}\n", | |
|
70 | "```\n", | |
|
71 | "### Display\n", | |
|
72 | "\\begin{equation*}\n", | |
|
73 | "\\mathbf{V}_1 \\times \\mathbf{V}_2 = \\begin{vmatrix}\n", | |
|
74 | "\\mathbf{i} & \\mathbf{j} & \\mathbf{k} \\\\\n", | |
|
75 | "\\frac{\\partial X}{\\partial u} & \\frac{\\partial Y}{\\partial u} & 0 \\\\\n", | |
|
76 | "\\frac{\\partial X}{\\partial v} & \\frac{\\partial Y}{\\partial v} & 0\n", | |
|
77 | "\\end{vmatrix} \n", | |
|
78 | "\\end{equation*}" | |
|
79 | ] | |
|
80 | }, | |
|
81 | { | |
|
82 | "cell_type": "markdown", | |
|
83 | "metadata": {}, | |
|
84 | "source": [ | |
|
85 | "## The probability of getting \\(k\\) heads when flipping \\(n\\) coins is\n", | |
|
86 | "### Source\n", | |
|
87 | "```\\begin{equation*}\n", | |
|
88 | "P(E) = {n \\choose k} p^k (1-p)^{ n-k} \n", | |
|
89 | "\\end{equation*}\n", | |
|
90 | "```\n", | |
|
91 | "### Display\n", | |
|
92 | "\\begin{equation*}\n", | |
|
93 | "P(E) = {n \\choose k} p^k (1-p)^{ n-k} \n", | |
|
94 | "\\end{equation*}" | |
|
95 | ] | |
|
96 | }, | |
|
97 | { | |
|
98 | "cell_type": "markdown", | |
|
99 | "metadata": {}, | |
|
100 | "source": [ | |
|
101 | "## An Identity of Ramanujan\n", | |
|
102 | "### Source\n", | |
|
103 | "```\\begin{equation*}\n", | |
|
104 | "\\frac{1}{\\Bigl(\\sqrt{\\phi \\sqrt{5}}-\\phi\\Bigr) e^{\\frac25 \\pi}} =\n", | |
|
105 | "1+\\frac{e^{-2\\pi}} {1+\\frac{e^{-4\\pi}} {1+\\frac{e^{-6\\pi}}\n", | |
|
106 | "{1+\\frac{e^{-8\\pi}} {1+\\ldots} } } } \n", | |
|
107 | "\\end{equation*}\n", | |
|
108 | "```\n", | |
|
109 | "### Display\n", | |
|
110 | "\\begin{equation*}\n", | |
|
111 | "\\frac{1}{\\Bigl(\\sqrt{\\phi \\sqrt{5}}-\\phi\\Bigr) e^{\\frac25 \\pi}} =\n", | |
|
112 | "1+\\frac{e^{-2\\pi}} {1+\\frac{e^{-4\\pi}} {1+\\frac{e^{-6\\pi}}\n", | |
|
113 | "{1+\\frac{e^{-8\\pi}} {1+\\ldots} } } } \n", | |
|
114 | "\\end{equation*}" | |
|
115 | ] | |
|
116 | }, | |
|
117 | { | |
|
118 | "cell_type": "markdown", | |
|
119 | "metadata": {}, | |
|
120 | "source": [ | |
|
121 | "## A Rogers-Ramanujan Identity\n", | |
|
122 | "### Source\n", | |
|
123 | "```\\begin{equation*}\n", | |
|
124 | "1 + \\frac{q^2}{(1-q)}+\\frac{q^6}{(1-q)(1-q^2)}+\\cdots =\n", | |
|
125 | "\\prod_{j=0}^{\\infty}\\frac{1}{(1-q^{5j+2})(1-q^{5j+3})},\n", | |
|
126 | "\\quad\\quad \\text{for $|q|<1$}. \n", | |
|
127 | "\\end{equation*}\n", | |
|
128 | "```\n", | |
|
129 | "### Display\n", | |
|
130 | "\\begin{equation*}\n", | |
|
131 | "1 + \\frac{q^2}{(1-q)}+\\frac{q^6}{(1-q)(1-q^2)}+\\cdots =\n", | |
|
132 | "\\prod_{j=0}^{\\infty}\\frac{1}{(1-q^{5j+2})(1-q^{5j+3})},\n", | |
|
133 | "\\quad\\quad \\text{for $|q|<1$}. \n", | |
|
134 | "\\end{equation*}" | |
|
135 | ] | |
|
136 | }, | |
|
137 | { | |
|
138 | "cell_type": "markdown", | |
|
139 | "metadata": {}, | |
|
140 | "source": [ | |
|
141 | "## Maxwell's Equations\n", | |
|
142 | "### Source\n", | |
|
143 | "```\\begin{aligned}\n", | |
|
144 | "\\nabla \\times \\vec{\\mathbf{B}} -\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{E}}}{\\partial t} & = \\frac{4\\pi}{c}\\vec{\\mathbf{j}} \\\\ \\nabla \\cdot \\vec{\\mathbf{E}} & = 4 \\pi \\rho \\\\\n", | |
|
145 | "\\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\\n", | |
|
146 | "\\nabla \\cdot \\vec{\\mathbf{B}} & = 0 \n", | |
|
147 | "\\end{aligned}\n", | |
|
148 | "```\n", | |
|
149 | "### Display\n", | |
|
150 | "\\begin{aligned}\n", | |
|
151 | "\\nabla \\times \\vec{\\mathbf{B}} -\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{E}}}{\\partial t} & = \\frac{4\\pi}{c}\\vec{\\mathbf{j}} \\\\ \\nabla \\cdot \\vec{\\mathbf{E}} & = 4 \\pi \\rho \\\\\n", | |
|
152 | "\\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\\n", | |
|
153 | "\\nabla \\cdot \\vec{\\mathbf{B}} & = 0 \n", | |
|
154 | "\\end{aligned}" | |
|
155 | ] | |
|
156 | }, | |
|
157 | { | |
|
158 | "cell_type": "markdown", | |
|
159 | "metadata": {}, | |
|
160 | "source": [ | |
|
161 | "# Equation Numbering and References\n", | |
|
162 | "\n", | |
|
163 | "---\n", | |
|
164 | "\n", | |
|
165 | "Equation numbering and referencing will be available in a future version of IPython." | |
|
166 | ] | |
|
167 | }, | |
|
168 | { | |
|
169 | "cell_type": "markdown", | |
|
170 | "metadata": {}, | |
|
171 | "source": [ | |
|
172 | "# Inline Typesetting (Mixing Markdown and TeX)\n", | |
|
173 | "\n", | |
|
174 | "---\n", | |
|
175 | "\n", | |
|
176 | "While display equations look good for a page of samples, the ability to mix math and *formatted* **text** in a paragraph is also important.\n", | |
|
177 | "\n", | |
|
178 | "## Source\n", | |
|
179 | "``` This expression $\\sqrt{3x-1}+(1+x)^2$ is an example of a TeX inline equation in a **[Markdown-formatted](http://daringfireball.net/projects/markdown/)** sentence. \n", | |
|
180 | "```\n", | |
|
181 | "## Display\n", | |
|
182 | "This expression $\\sqrt{3x-1}+(1+x)^2$ is an example of a TeX inline equation in a **[Markdown-formatted](http://daringfireball.net/projects/markdown/)** sentence. " | |
|
183 | ] | |
|
184 | }, | |
|
185 | { | |
|
186 | "cell_type": "markdown", | |
|
187 | "metadata": {}, | |
|
188 | "source": [ | |
|
189 | "# Other Syntax\n", | |
|
190 | "\n", | |
|
191 | "---\n", | |
|
192 | "\n", | |
|
193 | "You will notice in other places on the web that `$$` are needed explicitly to begin and end MathJax typesetting. This is **not** required if you will be using TeX environments, but the IPython notebook will accept this syntax on legacy notebooks. \n", | |
|
194 | "\n", | |
|
195 | "### Source\n", | |
|
196 | "```$$\n", | |
|
197 | "\\begin{array}{c}\n", | |
|
198 | "y_1 \\\\\\\n", | |
|
199 | "y_2 \\mathtt{t}_i \\\\\\\n", | |
|
200 | "z_{3,4}\n", | |
|
201 | "\\end{array}\n", | |
|
202 | "$$\n", | |
|
203 | "```\n", | |
|
204 | "\n", | |
|
205 | "```\n", | |
|
206 | "$$\n", | |
|
207 | "\\begin{array}{c}\n", | |
|
208 | "y_1 \\cr\n", | |
|
209 | "y_2 \\mathtt{t}_i \\cr\n", | |
|
210 | "y_{3}\n", | |
|
211 | "\\end{array}\n", | |
|
212 | "$$\n", | |
|
213 | "```\n", | |
|
214 | "\n", | |
|
215 | "```\n", | |
|
216 | "$$\\begin{eqnarray} \n", | |
|
217 | "x' &=& &x \\sin\\phi &+& z \\cos\\phi \\\\\n", | |
|
218 | "z' &=& - &x \\cos\\phi &+& z \\sin\\phi \\\\\n", | |
|
219 | "\\end{eqnarray}$$\n", | |
|
220 | "```\n", | |
|
221 | "\n", | |
|
222 | "```\n", | |
|
223 | "$$\n", | |
|
224 | "x=4\n", | |
|
225 | "$$\n", | |
|
226 | "```\n", | |
|
227 | "\n", | |
|
228 | "### Display\n", | |
|
229 | "$$\n", | |
|
230 | "\\begin{array}{c}\n", | |
|
231 | "y_1 \\\\\\\n", | |
|
232 | "y_2 \\mathtt{t}_i \\\\\\\n", | |
|
233 | "z_{3,4}\n", | |
|
234 | "\\end{array}\n", | |
|
235 | "$$\n", | |
|
236 | "\n", | |
|
237 | "$$\n", | |
|
238 | "\\begin{array}{c}\n", | |
|
239 | "y_1 \\cr\n", | |
|
240 | "y_2 \\mathtt{t}_i \\cr\n", | |
|
241 | "y_{3}\n", | |
|
242 | "\\end{array}\n", | |
|
243 | "$$\n", | |
|
244 | "\n", | |
|
245 | "$$\\begin{eqnarray} \n", | |
|
246 | "x' &=& &x \\sin\\phi &+& z \\cos\\phi \\\\\n", | |
|
247 | "z' &=& - &x \\cos\\phi &+& z \\sin\\phi \\\\\n", | |
|
248 | "\\end{eqnarray}$$\n", | |
|
249 | "\n", | |
|
250 | "$$\n", | |
|
251 | "x=4\n", | |
|
252 | "$$" | |
|
253 | ] | |
|
254 | } | |
|
255 | ], | |
|
256 | "metadata": {} | |
|
257 | } | |
|
258 | ] | |
|
259 | } No newline at end of file |
@@ -0,0 +1,90 b'' | |||
|
1 | #!/usr/bin/env python | |
|
2 | """ | |
|
3 | Backport pull requests to a particular branch. | |
|
4 | ||
|
5 | Usage: backport_pr.py branch PR | |
|
6 | ||
|
7 | e.g.: | |
|
8 | ||
|
9 | backport_pr.py 0.13.1 123 | |
|
10 | ||
|
11 | to backport PR #123 onto branch 0.13.1 | |
|
12 | ||
|
13 | """ | |
|
14 | ||
|
15 | from __future__ import print_function | |
|
16 | ||
|
17 | import os | |
|
18 | import sys | |
|
19 | from subprocess import Popen, PIPE, check_call, check_output | |
|
20 | from urllib import urlopen | |
|
21 | ||
|
22 | from gh_api import get_pull_request | |
|
23 | ||
|
24 | def find_rejects(root='.'): | |
|
25 | for dirname, dirs, files in os.walk(root): | |
|
26 | for fname in files: | |
|
27 | if fname.endswith('.rej'): | |
|
28 | yield os.path.join(dirname, fname) | |
|
29 | ||
|
30 | def get_current_branch(): | |
|
31 | branches = check_output(['git', 'branch']) | |
|
32 | for branch in branches.splitlines(): | |
|
33 | if branch.startswith('*'): | |
|
34 | return branch[1:].strip() | |
|
35 | ||
|
36 | def backport_pr(branch, num, project='ipython/ipython'): | |
|
37 | current_branch = get_current_branch() | |
|
38 | if branch != current_branch: | |
|
39 | check_call(['git', 'checkout', branch]) | |
|
40 | pr = get_pull_request(project, num) | |
|
41 | patch_url = pr['patch_url'] | |
|
42 | title = pr['title'] | |
|
43 | description = pr['body'] | |
|
44 | fname = "PR%i.patch" % num | |
|
45 | if os.path.exists(fname): | |
|
46 | print("using patch from {fname}".format(**locals())) | |
|
47 | with open(fname) as f: | |
|
48 | patch = f.read() | |
|
49 | else: | |
|
50 | req = urlopen(patch_url) | |
|
51 | patch = req.read() | |
|
52 | ||
|
53 | msg = "Backport PR #%i: %s" % (num, title) + '\n\n' + description | |
|
54 | check = Popen(['git', 'apply', '--check', '--verbose'], stdin=PIPE) | |
|
55 | a,b = check.communicate(patch) | |
|
56 | ||
|
57 | if check.returncode: | |
|
58 | print("patch did not apply, saving to {fname}".format(**locals())) | |
|
59 | print("edit {fname} until `cat {fname} | git apply --check` succeeds".format(**locals())) | |
|
60 | print("then run tools/backport_pr.py {num} again".format(**locals())) | |
|
61 | if not os.path.exists(fname): | |
|
62 | with open(fname, 'wb') as f: | |
|
63 | f.write(patch) | |
|
64 | return 1 | |
|
65 | ||
|
66 | p = Popen(['git', 'apply'], stdin=PIPE) | |
|
67 | a,b = p.communicate(patch) | |
|
68 | ||
|
69 | commit = Popen(['git', 'commit', '-a', '-m', msg]) | |
|
70 | commit.communicate() | |
|
71 | if commit.returncode: | |
|
72 | print("commit failed!") | |
|
73 | return 1 | |
|
74 | else: | |
|
75 | print("PR #%i applied, with msg:" % num) | |
|
76 | print() | |
|
77 | print(msg) | |
|
78 | print() | |
|
79 | ||
|
80 | if branch != current_branch: | |
|
81 | check_call(['git', 'checkout', current_branch]) | |
|
82 | ||
|
83 | return 0 | |
|
84 | ||
|
85 | if __name__ == '__main__': | |
|
86 | if len(sys.argv) < 3: | |
|
87 | print(__doc__) | |
|
88 | sys.exit(1) | |
|
89 | ||
|
90 | sys.exit(backport_pr(sys.argv[1], int(sys.argv[2]))) |
@@ -7,6 +7,7 b' docs/source/api/generated' | |||
|
7 | 7 | docs/gh-pages |
|
8 | 8 | IPython/frontend/html/notebook/static/mathjax |
|
9 | 9 | *.py[co] |
|
10 | __pycache__ | |
|
10 | 11 | build |
|
11 | 12 | *.egg-info |
|
12 | 13 | *~ |
@@ -57,6 +57,7 b' for author, email in release.authors.itervalues():' | |||
|
57 | 57 | __author__ += author + ' <' + email + '>\n' |
|
58 | 58 | __license__ = release.license |
|
59 | 59 | __version__ = release.version |
|
60 | version_info = release.version_info | |
|
60 | 61 | |
|
61 | 62 | def embed_kernel(module=None, local_ns=None, **kwargs): |
|
62 | 63 | """Embed and start an IPython kernel in a given scope. |
@@ -181,7 +181,7 b' class BaseIPythonApplication(Application):' | |||
|
181 | 181 | sys.path.remove(old) |
|
182 | 182 | sys.path.append(os.path.abspath(new)) |
|
183 | 183 | if not os.path.isdir(new): |
|
184 | os.makedirs(new, mode=0777) | |
|
184 | os.makedirs(new, mode=0o777) | |
|
185 | 185 | readme = os.path.join(new, 'README') |
|
186 | 186 | if not os.path.exists(readme): |
|
187 | 187 | path = os.path.join(get_ipython_package_dir(), u'config', u'profile') |
@@ -17,6 +17,7 b' from __future__ import print_function' | |||
|
17 | 17 | |
|
18 | 18 | # Stdlib imports |
|
19 | 19 | import glob |
|
20 | import imp | |
|
20 | 21 | import inspect |
|
21 | 22 | import os |
|
22 | 23 | import re |
@@ -90,7 +91,8 b' def module_list(path):' | |||
|
90 | 91 | |
|
91 | 92 | # Now find actual path matches for packages or modules |
|
92 | 93 | folder_list = [p for p in folder_list |
|
93 |
if isfile(pjoin(path, p,'__init__ |
|
|
94 | if any(isfile(pjoin(path, p, '__init__' + suffix[0])) for | |
|
95 | suffix in imp.get_suffixes()) | |
|
94 | 96 | or is_importable_file(p) ] |
|
95 | 97 | |
|
96 | 98 | return [basename(p).split('.')[0] for p in folder_list] |
@@ -30,9 +30,9 b' import bdb' | |||
|
30 | 30 | import linecache |
|
31 | 31 | import sys |
|
32 | 32 | |
|
33 | from IPython.utils import PyColorize | |
|
33 | from IPython.utils import PyColorize, ulinecache | |
|
34 | 34 | from IPython.core import ipapi |
|
35 | from IPython.utils import coloransi, io | |
|
35 | from IPython.utils import coloransi, io, openpy, py3compat | |
|
36 | 36 | from IPython.core.excolors import exception_colors |
|
37 | 37 | |
|
38 | 38 | # See if we can use pydb. |
@@ -304,16 +304,16 b' class Pdb(OldPdb):' | |||
|
304 | 304 | # vds: << |
|
305 | 305 | |
|
306 | 306 | def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3): |
|
307 |
import |
|
|
307 | import repr | |
|
308 | 308 | |
|
309 | 309 | ret = [] |
|
310 | 310 | |
|
311 | 311 | Colors = self.color_scheme_table.active_colors |
|
312 | 312 | ColorsNormal = Colors.Normal |
|
313 | tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal) | |
|
314 | tpl_call = '%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal) | |
|
315 | tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) | |
|
316 | tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, | |
|
313 | tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal) | |
|
314 | tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal) | |
|
315 | tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) | |
|
316 | tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, | |
|
317 | 317 | ColorsNormal) |
|
318 | 318 | |
|
319 | 319 | frame, lineno = frame_lineno |
@@ -327,7 +327,7 b' class Pdb(OldPdb):' | |||
|
327 | 327 | |
|
328 | 328 | #s = filename + '(' + `lineno` + ')' |
|
329 | 329 | filename = self.canonic(frame.f_code.co_filename) |
|
330 | link = tpl_link % filename | |
|
330 | link = tpl_link % py3compat.cast_unicode(filename) | |
|
331 | 331 | |
|
332 | 332 | if frame.f_code.co_name: |
|
333 | 333 | func = frame.f_code.co_name |
@@ -348,10 +348,10 b' class Pdb(OldPdb):' | |||
|
348 | 348 | ret.append('> ') |
|
349 | 349 | else: |
|
350 | 350 | ret.append(' ') |
|
351 | ret.append('%s(%s)%s\n' % (link,lineno,call)) | |
|
351 | ret.append(u'%s(%s)%s\n' % (link,lineno,call)) | |
|
352 | 352 | |
|
353 | 353 | start = lineno - 1 - context//2 |
|
354 | lines = linecache.getlines(filename) | |
|
354 | lines = ulinecache.getlines(filename) | |
|
355 | 355 | start = max(start, 0) |
|
356 | 356 | start = min(start, len(lines) - context) |
|
357 | 357 | lines = lines[start : start + context] |
@@ -364,7 +364,6 b' class Pdb(OldPdb):' | |||
|
364 | 364 | ret.append(self.__format_line(linetpl, filename, |
|
365 | 365 | start + 1 + i, line, |
|
366 | 366 | arrow = show_arrow) ) |
|
367 | ||
|
368 | 367 | return ''.join(ret) |
|
369 | 368 | |
|
370 | 369 | def __format_line(self, tpl_line, filename, lineno, line, arrow = False): |
@@ -422,8 +421,11 b' class Pdb(OldPdb):' | |||
|
422 | 421 | tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) |
|
423 | 422 | tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal) |
|
424 | 423 | src = [] |
|
424 | if filename == "<string>" and hasattr(self, "_exec_filename"): | |
|
425 | filename = self._exec_filename | |
|
426 | ||
|
425 | 427 | for lineno in range(first, last+1): |
|
426 | line = linecache.getline(filename, lineno) | |
|
428 | line = ulinecache.getline(filename, lineno) | |
|
427 | 429 | if not line: |
|
428 | 430 | break |
|
429 | 431 | |
@@ -475,23 +477,51 b' class Pdb(OldPdb):' | |||
|
475 | 477 | do_l = do_list |
|
476 | 478 | |
|
477 | 479 | def do_pdef(self, arg): |
|
478 | """The debugger interface to magic_pdef""" | |
|
480 | """Print the call signature for any callable object. | |
|
481 | ||
|
482 | The debugger interface to %pdef""" | |
|
479 | 483 | namespaces = [('Locals', self.curframe.f_locals), |
|
480 | 484 | ('Globals', self.curframe.f_globals)] |
|
481 | 485 | self.shell.find_line_magic('pdef')(arg, namespaces=namespaces) |
|
482 | 486 | |
|
483 | 487 | def do_pdoc(self, arg): |
|
484 | """The debugger interface to magic_pdoc""" | |
|
488 | """Print the docstring for an object. | |
|
489 | ||
|
490 | The debugger interface to %pdoc.""" | |
|
485 | 491 | namespaces = [('Locals', self.curframe.f_locals), |
|
486 | 492 | ('Globals', self.curframe.f_globals)] |
|
487 | 493 | self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces) |
|
488 | 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 | ||
|
489 | 504 | def do_pinfo(self, arg): |
|
490 | """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.""" | |
|
491 | 522 | namespaces = [('Locals', self.curframe.f_locals), |
|
492 | 523 | ('Globals', self.curframe.f_globals)] |
|
493 |
self.shell.find_line_magic('p |
|
|
494 | namespaces=namespaces) | |
|
524 | self.shell.find_line_magic('psource')(arg, namespaces=namespaces) | |
|
495 | 525 | |
|
496 | 526 | def checkline(self, filename, lineno): |
|
497 | 527 | """Check whether specified line seems to be executable. |
@@ -25,6 +25,9 b' from __future__ import print_function' | |||
|
25 | 25 | |
|
26 | 26 | import __builtin__ |
|
27 | 27 | |
|
28 | import sys | |
|
29 | ||
|
30 | ||
|
28 | 31 | from IPython.config.configurable import Configurable |
|
29 | 32 | from IPython.utils import io |
|
30 | 33 | from IPython.utils.traitlets import Instance, List |
@@ -266,5 +269,7 b' class DisplayHook(Configurable):' | |||
|
266 | 269 | self.shell.user_ns.update({'_':None,'__':None, '___':None}) |
|
267 | 270 | import gc |
|
268 | 271 | # TODO: Is this really needed? |
|
269 | gc.collect() | |
|
272 | # IronPython blocks here forever | |
|
273 | if sys.platform != "cli": | |
|
274 | gc.collect() | |
|
270 | 275 |
@@ -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( |
@@ -75,31 +81,48 b' class ExtensionManager(Configurable):' | |||
|
75 | 81 | |
|
76 | 82 | def _on_ipython_dir_changed(self): |
|
77 | 83 | if not os.path.isdir(self.ipython_extension_dir): |
|
78 | os.makedirs(self.ipython_extension_dir, mode = 0777) | |
|
84 | os.makedirs(self.ipython_extension_dir, mode = 0o777) | |
|
79 | 85 | |
|
80 | 86 | def load_extension(self, module_str): |
|
81 | 87 | """Load an IPython extension by its module name. |
|
82 | 88 | |
|
83 | If :func:`load_ipython_extension` returns anything, this function | |
|
84 | will return that object. | |
|
89 | Returns the string "already loaded" if the extension is already loaded, | |
|
90 | "no load function" if the module doesn't have a load_ipython_extension | |
|
91 | function, or None if it succeeded. | |
|
85 | 92 | """ |
|
93 | if module_str in self.loaded: | |
|
94 | return "already loaded" | |
|
95 | ||
|
86 | 96 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
87 | 97 | |
|
88 | 98 | if module_str not in sys.modules: |
|
89 | 99 | with prepended_to_syspath(self.ipython_extension_dir): |
|
90 | 100 | __import__(module_str) |
|
91 | 101 | mod = sys.modules[module_str] |
|
92 |
|
|
|
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. |
@@ -138,7 +165,7 b' class ExtensionManager(Configurable):' | |||
|
138 | 165 | """ |
|
139 | 166 | # Ensure the extension directory exists |
|
140 | 167 | if not os.path.isdir(self.ipython_extension_dir): |
|
141 | os.makedirs(self.ipython_extension_dir, mode = 0777) | |
|
168 | os.makedirs(self.ipython_extension_dir, mode = 0o777) | |
|
142 | 169 | |
|
143 | 170 | if os.path.isfile(url): |
|
144 | 171 | src_filename = os.path.basename(url) |
@@ -62,6 +62,37 b' def needs_sqlite(f, self, *a, **kw):' | |||
|
62 | 62 | return f(self, *a, **kw) |
|
63 | 63 | |
|
64 | 64 | |
|
65 | if sqlite3 is not None: | |
|
66 | DatabaseError = sqlite3.DatabaseError | |
|
67 | else: | |
|
68 | class DatabaseError(Exception): | |
|
69 | "Dummy exception when sqlite could not be imported. Should never occur." | |
|
70 | ||
|
71 | @decorator | |
|
72 | def catch_corrupt_db(f, self, *a, **kw): | |
|
73 | """A decorator which wraps HistoryAccessor method calls to catch errors from | |
|
74 | a corrupt SQLite database, move the old database out of the way, and create | |
|
75 | a new one. | |
|
76 | """ | |
|
77 | try: | |
|
78 | return f(self, *a, **kw) | |
|
79 | except DatabaseError: | |
|
80 | if os.path.isfile(self.hist_file): | |
|
81 | # Try to move the file out of the way | |
|
82 | base,ext = os.path.splitext(self.hist_file) | |
|
83 | newpath = base + '-corrupt' + ext | |
|
84 | os.rename(self.hist_file, newpath) | |
|
85 | self.init_db() | |
|
86 | print("ERROR! History file wasn't a valid SQLite database.", | |
|
87 | "It was moved to %s" % newpath, "and a new file created.") | |
|
88 | return [] | |
|
89 | ||
|
90 | else: | |
|
91 | # The hist_file is probably :memory: or something else. | |
|
92 | raise | |
|
93 | ||
|
94 | ||
|
95 | ||
|
65 | 96 | class HistoryAccessor(Configurable): |
|
66 | 97 | """Access the history database without adding to it. |
|
67 | 98 | |
@@ -143,25 +174,7 b' class HistoryAccessor(Configurable):' | |||
|
143 | 174 | warn("IPython History requires SQLite, your history will not be saved\n") |
|
144 | 175 | self.enabled = False |
|
145 | 176 | |
|
146 | if sqlite3 is not None: | |
|
147 | DatabaseError = sqlite3.DatabaseError | |
|
148 | else: | |
|
149 | DatabaseError = Exception | |
|
150 | ||
|
151 | try: | |
|
152 | self.init_db() | |
|
153 | except DatabaseError: | |
|
154 | if os.path.isfile(self.hist_file): | |
|
155 | # Try to move the file out of the way | |
|
156 | base,ext = os.path.splitext(self.hist_file) | |
|
157 | newpath = base + '-corrupt' + ext | |
|
158 | os.rename(self.hist_file, newpath) | |
|
159 | print("ERROR! History file wasn't a valid SQLite database.", | |
|
160 | "It was moved to %s" % newpath, "and a new file created.") | |
|
161 | self.init_db() | |
|
162 | else: | |
|
163 | # The hist_file is probably :memory: or something else. | |
|
164 | raise | |
|
177 | self.init_db() | |
|
165 | 178 | |
|
166 | 179 | def _get_hist_file_name(self, profile='default'): |
|
167 | 180 | """Find the history file for the given profile name. |
@@ -176,6 +189,7 b' class HistoryAccessor(Configurable):' | |||
|
176 | 189 | """ |
|
177 | 190 | return os.path.join(locate_profile(profile), 'history.sqlite') |
|
178 | 191 | |
|
192 | @catch_corrupt_db | |
|
179 | 193 | def init_db(self): |
|
180 | 194 | """Connect to the database, and create tables if necessary.""" |
|
181 | 195 | if not self.enabled: |
@@ -235,6 +249,7 b' class HistoryAccessor(Configurable):' | |||
|
235 | 249 | return cur |
|
236 | 250 | |
|
237 | 251 | @needs_sqlite |
|
252 | @catch_corrupt_db | |
|
238 | 253 | def get_session_info(self, session=0): |
|
239 | 254 | """get info about a session |
|
240 | 255 | |
@@ -262,6 +277,7 b' class HistoryAccessor(Configurable):' | |||
|
262 | 277 | query = "SELECT * from sessions where session == ?" |
|
263 | 278 | return self.db.execute(query, (session,)).fetchone() |
|
264 | 279 | |
|
280 | @catch_corrupt_db | |
|
265 | 281 | def get_tail(self, n=10, raw=True, output=False, include_latest=False): |
|
266 | 282 | """Get the last n lines from the history database. |
|
267 | 283 | |
@@ -289,8 +305,9 b' class HistoryAccessor(Configurable):' | |||
|
289 | 305 | return reversed(list(cur)[1:]) |
|
290 | 306 | return reversed(list(cur)) |
|
291 | 307 | |
|
308 | @catch_corrupt_db | |
|
292 | 309 | def search(self, pattern="*", raw=True, search_raw=True, |
|
293 | output=False): | |
|
310 | output=False, n=None): | |
|
294 | 311 | """Search the database using unix glob-style matching (wildcards |
|
295 | 312 | * and ?). |
|
296 | 313 | |
@@ -302,6 +319,9 b' class HistoryAccessor(Configurable):' | |||
|
302 | 319 | If True, search the raw input, otherwise, the parsed input |
|
303 | 320 | raw, output : bool |
|
304 | 321 | See :meth:`get_range` |
|
322 | n : None or int | |
|
323 | If an integer is given, it defines the limit of | |
|
324 | returned entries. | |
|
305 | 325 | |
|
306 | 326 | Returns |
|
307 | 327 | ------- |
@@ -311,9 +331,17 b' class HistoryAccessor(Configurable):' | |||
|
311 | 331 | if output: |
|
312 | 332 | tosearch = "history." + tosearch |
|
313 | 333 | self.writeout_cache() |
|
314 |
|
|
|
315 | raw=raw, output=output) | |
|
334 | sqlform = "WHERE %s GLOB ?" % tosearch | |
|
335 | params = (pattern,) | |
|
336 | if n is not None: | |
|
337 | sqlform += " ORDER BY session DESC, line DESC LIMIT ?" | |
|
338 | params += (n,) | |
|
339 | cur = self._run_sql(sqlform, params, raw=raw, output=output) | |
|
340 | if n is not None: | |
|
341 | return reversed(list(cur)) | |
|
342 | return cur | |
|
316 | 343 | |
|
344 | @catch_corrupt_db | |
|
317 | 345 | def get_range(self, session, start=1, stop=None, raw=True,output=False): |
|
318 | 346 | """Retrieve input by session. |
|
319 | 347 | |
@@ -347,7 +375,7 b' class HistoryAccessor(Configurable):' | |||
|
347 | 375 | lineclause = "line>=?" |
|
348 | 376 | params = (session, start) |
|
349 | 377 | |
|
350 |
return self._run_sql("WHERE session==? AND %s" |
|
|
378 | return self._run_sql("WHERE session==? AND %s" % lineclause, | |
|
351 | 379 | params, raw=raw, output=output) |
|
352 | 380 | |
|
353 | 381 | def get_range_by_str(self, rangestr, raw=True, output=False): |
@@ -29,17 +29,11 b' import runpy' | |||
|
29 | 29 | import sys |
|
30 | 30 | import tempfile |
|
31 | 31 | import types |
|
32 | ||
|
33 | # We need to use nested to support python 2.6, once we move to >=2.7, we can | |
|
34 | # use the with keyword's new builtin support for nested managers | |
|
35 | try: | |
|
36 | from contextlib import nested | |
|
37 | except: | |
|
38 | from IPython.utils.nested_context import nested | |
|
32 | import urllib | |
|
33 | from io import open as io_open | |
|
39 | 34 | |
|
40 | 35 | from IPython.config.configurable import SingletonConfigurable |
|
41 | 36 | from IPython.core import debugger, oinspect |
|
42 | from IPython.core import history as ipcorehist | |
|
43 | 37 | from IPython.core import magic |
|
44 | 38 | from IPython.core import page |
|
45 | 39 | from IPython.core import prefilter |
@@ -61,7 +55,6 b' from IPython.core.inputsplitter import IPythonInputSplitter, ESC_MAGIC, ESC_MAGI' | |||
|
61 | 55 | from IPython.core.logger import Logger |
|
62 | 56 | from IPython.core.macro import Macro |
|
63 | 57 | from IPython.core.payload import PayloadManager |
|
64 | from IPython.core.plugin import PluginManager | |
|
65 | 58 | from IPython.core.prefilter import PrefilterManager |
|
66 | 59 | from IPython.core.profiledir import ProfileDir |
|
67 | 60 | from IPython.core.pylabtools import pylab_activate |
@@ -392,7 +385,6 b' class InteractiveShell(SingletonConfigurable):' | |||
|
392 | 385 | builtin_trap = Instance('IPython.core.builtin_trap.BuiltinTrap') |
|
393 | 386 | display_trap = Instance('IPython.core.display_trap.DisplayTrap') |
|
394 | 387 | extension_manager = Instance('IPython.core.extensions.ExtensionManager') |
|
395 | plugin_manager = Instance('IPython.core.plugin.PluginManager') | |
|
396 | 388 | payload_manager = Instance('IPython.core.payload.PayloadManager') |
|
397 | 389 | history_manager = Instance('IPython.core.history.HistoryManager') |
|
398 | 390 | magics_manager = Instance('IPython.core.magic.MagicsManager') |
@@ -492,7 +484,6 b' class InteractiveShell(SingletonConfigurable):' | |||
|
492 | 484 | self.init_logstart() |
|
493 | 485 | self.init_pdb() |
|
494 | 486 | self.init_extension_manager() |
|
495 | self.init_plugin_manager() | |
|
496 | 487 | self.init_payload() |
|
497 | 488 | self.hooks.late_startup_hook() |
|
498 | 489 | atexit.register(self.atexit_operations) |
@@ -507,7 +498,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
507 | 498 | |
|
508 | 499 | def _ipython_dir_changed(self, name, new): |
|
509 | 500 | if not os.path.isdir(new): |
|
510 | os.makedirs(new, mode = 0777) | |
|
501 | os.makedirs(new, mode = 0o777) | |
|
511 | 502 | |
|
512 | 503 | def set_autoindent(self,value=None): |
|
513 | 504 | """Set the autoindent flag, checking for readline support. |
@@ -638,7 +629,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
638 | 629 | # override sys.stdout and sys.stderr themselves, you need to do that |
|
639 | 630 | # *before* instantiating this class, because io holds onto |
|
640 | 631 | # references to the underlying streams. |
|
641 | if sys.platform == 'win32' and self.has_readline: | |
|
632 | if (sys.platform == 'win32' or sys.platform == 'cli') and self.has_readline: | |
|
642 | 633 | io.stdout = io.stderr = io.IOStream(self.readline._outputfile) |
|
643 | 634 | else: |
|
644 | 635 | io.stdout = io.IOStream(sys.stdout) |
@@ -1722,7 +1713,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
1722 | 1713 | self.write_err('No traceback available to show.\n') |
|
1723 | 1714 | return |
|
1724 | 1715 | |
|
1725 |
if etype |
|
|
1716 | if issubclass(etype, SyntaxError): | |
|
1726 | 1717 | # Though this won't be called by syntax errors in the input |
|
1727 | 1718 | # line, there may be SyntaxError cases with imported code. |
|
1728 | 1719 | self.showsyntaxerror(filename) |
@@ -1775,7 +1766,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
1775 | 1766 | """ |
|
1776 | 1767 | etype, value, last_traceback = self._get_exc_info() |
|
1777 | 1768 | |
|
1778 |
if filename and etype |
|
|
1769 | if filename and issubclass(etype, SyntaxError): | |
|
1779 | 1770 | try: |
|
1780 | 1771 | value.filename = filename |
|
1781 | 1772 | except: |
@@ -2288,18 +2279,13 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2288 | 2279 | self.ns_table['alias'] = self.alias_manager.alias_table, |
|
2289 | 2280 | |
|
2290 | 2281 | #------------------------------------------------------------------------- |
|
2291 |
# Things related to extensions |
|
|
2282 | # Things related to extensions | |
|
2292 | 2283 | #------------------------------------------------------------------------- |
|
2293 | 2284 | |
|
2294 | 2285 | def init_extension_manager(self): |
|
2295 | 2286 | self.extension_manager = ExtensionManager(shell=self, config=self.config) |
|
2296 | 2287 | self.configurables.append(self.extension_manager) |
|
2297 | 2288 | |
|
2298 | def init_plugin_manager(self): | |
|
2299 | self.plugin_manager = PluginManager(config=self.config) | |
|
2300 | self.configurables.append(self.plugin_manager) | |
|
2301 | ||
|
2302 | ||
|
2303 | 2289 | #------------------------------------------------------------------------- |
|
2304 | 2290 | # Things related to payloads |
|
2305 | 2291 | #------------------------------------------------------------------------- |
@@ -2461,9 +2447,6 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2461 | 2447 | dname = os.path.dirname(fname) |
|
2462 | 2448 | |
|
2463 | 2449 | with prepended_to_syspath(dname): |
|
2464 | # Ensure that __file__ is always defined to match Python behavior | |
|
2465 | save_fname = self.user_ns.get('__file__',None) | |
|
2466 | self.user_ns['__file__'] = fname | |
|
2467 | 2450 | try: |
|
2468 | 2451 | py3compat.execfile(fname,*where) |
|
2469 | 2452 | except SystemExit as status: |
@@ -2484,8 +2467,6 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2484 | 2467 | if kw['raise_exceptions']: |
|
2485 | 2468 | raise |
|
2486 | 2469 | self.showtraceback() |
|
2487 | finally: | |
|
2488 | self.user_ns['__file__'] = save_fname | |
|
2489 | 2470 | |
|
2490 | 2471 | def safe_execfile_ipy(self, fname): |
|
2491 | 2472 | """Like safe_execfile, but for .ipy files with IPython syntax. |
@@ -2512,9 +2493,6 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2512 | 2493 | dname = os.path.dirname(fname) |
|
2513 | 2494 | |
|
2514 | 2495 | with prepended_to_syspath(dname): |
|
2515 | # Ensure that __file__ is always defined to match Python behavior | |
|
2516 | save_fname = self.user_ns.get('__file__',None) | |
|
2517 | self.user_ns['__file__'] = fname | |
|
2518 | 2496 | try: |
|
2519 | 2497 | with open(fname) as thefile: |
|
2520 | 2498 | # self.run_cell currently captures all exceptions |
@@ -2525,8 +2503,6 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2525 | 2503 | except: |
|
2526 | 2504 | self.showtraceback() |
|
2527 | 2505 | warn('Unknown failure executing file: <%s>' % fname) |
|
2528 | finally: | |
|
2529 | self.user_ns['__file__'] = save_fname | |
|
2530 | 2506 | |
|
2531 | 2507 | def safe_run_module(self, mod_name, where): |
|
2532 | 2508 | """A safe version of runpy.run_module(). |
@@ -2914,7 +2890,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2914 | 2890 | lines = self.history_manager.get_range_by_str(range_str, raw=raw) |
|
2915 | 2891 | return "\n".join(x for _, _, x in lines) |
|
2916 | 2892 | |
|
2917 | def find_user_code(self, target, raw=True, py_only=False): | |
|
2893 | def find_user_code(self, target, raw=True, py_only=False, skip_encoding_cookie=True): | |
|
2918 | 2894 | """Get a code string from history, file, url, or a string or macro. |
|
2919 | 2895 | |
|
2920 | 2896 | This is mainly used by magic functions. |
@@ -2951,7 +2927,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2951 | 2927 | utarget = unquote_filename(target) |
|
2952 | 2928 | try: |
|
2953 | 2929 | if utarget.startswith(('http://', 'https://')): |
|
2954 |
return openpy.read_py_url(utarget, skip_encoding_cookie= |
|
|
2930 | return openpy.read_py_url(utarget, skip_encoding_cookie=skip_encoding_cookie) | |
|
2955 | 2931 | except UnicodeDecodeError: |
|
2956 | 2932 | if not py_only : |
|
2957 | 2933 | response = urllib.urlopen(target) |
@@ -2967,7 +2943,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2967 | 2943 | for tgt in potential_target : |
|
2968 | 2944 | if os.path.isfile(tgt): # Read file |
|
2969 | 2945 | try : |
|
2970 |
return openpy.read_py_file(tgt, skip_encoding_cookie= |
|
|
2946 | return openpy.read_py_file(tgt, skip_encoding_cookie=skip_encoding_cookie) | |
|
2971 | 2947 | except UnicodeDecodeError : |
|
2972 | 2948 | if not py_only : |
|
2973 | 2949 | with io_open(tgt,'r', encoding='latin1') as f : |
@@ -66,7 +66,6 b' class MagicArgumentParser(argparse.ArgumentParser):' | |||
|
66 | 66 | usage=None, |
|
67 | 67 | description=None, |
|
68 | 68 | epilog=None, |
|
69 | version=None, | |
|
70 | 69 | parents=None, |
|
71 | 70 | formatter_class=MagicHelpFormatter, |
|
72 | 71 | prefix_chars='-', |
@@ -76,7 +75,7 b' class MagicArgumentParser(argparse.ArgumentParser):' | |||
|
76 | 75 | if parents is None: |
|
77 | 76 | parents = [] |
|
78 | 77 | super(MagicArgumentParser, self).__init__(prog=prog, usage=usage, |
|
79 |
description=description, epilog=epilog, |
|
|
78 | description=description, epilog=epilog, | |
|
80 | 79 | parents=parents, formatter_class=formatter_class, |
|
81 | 80 | prefix_chars=prefix_chars, argument_default=argument_default, |
|
82 | 81 | conflict_handler=conflict_handler, add_help=add_help) |
@@ -174,11 +173,17 b' class magic_arguments(ArgDecorator):' | |||
|
174 | 173 | return func |
|
175 | 174 | |
|
176 | 175 | |
|
177 |
class |
|
|
178 | """ 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`. | |
|
179 | 182 | |
|
180 | Instances also serve to decorate command methods. | |
|
181 | 183 | """ |
|
184 | ||
|
185 | _method_name = None | |
|
186 | ||
|
182 | 187 | def __init__(self, *args, **kwds): |
|
183 | 188 | self.args = args |
|
184 | 189 | self.kwds = kwds |
@@ -188,18 +193,31 b' class argument(ArgDecorator):' | |||
|
188 | 193 | """ |
|
189 | 194 | if group is not None: |
|
190 | 195 | parser = group |
|
191 |
parser. |
|
|
196 | getattr(parser, self._method_name)(*self.args, **self.kwds) | |
|
192 | 197 | return None |
|
193 | 198 | |
|
194 | 199 | |
|
195 |
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): | |
|
196 | 217 | """ Store arguments and keywords to pass to add_argument_group(). |
|
197 | 218 | |
|
198 | 219 | Instances also serve to decorate command methods. |
|
199 | 220 | """ |
|
200 | def __init__(self, *args, **kwds): | |
|
201 | self.args = args | |
|
202 | self.kwds = kwds | |
|
203 | 221 | |
|
204 | 222 | def add_to_parser(self, parser, group): |
|
205 | 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: |
@@ -44,10 +44,11 b' from IPython.utils import py3compat' | |||
|
44 | 44 | from IPython.utils.io import capture_output |
|
45 | 45 | from IPython.utils.ipstruct import Struct |
|
46 | 46 | from IPython.utils.module_paths import find_mod |
|
47 | from IPython.utils.path import get_py_filename, unquote_filename | |
|
47 | from IPython.utils.path import get_py_filename, unquote_filename, shellglob | |
|
48 | 48 | from IPython.utils.timing import clock, clock2 |
|
49 | 49 | from IPython.utils.warn import warn, error |
|
50 | 50 | |
|
51 | ||
|
51 | 52 | #----------------------------------------------------------------------------- |
|
52 | 53 | # Magic implementation classes |
|
53 | 54 | #----------------------------------------------------------------------------- |
@@ -324,7 +325,7 b' python-profiler package from non-free.""")' | |||
|
324 | 325 | """Run the named file inside IPython as a program. |
|
325 | 326 | |
|
326 | 327 | Usage:\\ |
|
327 | %run [-n -i -t [-N<N>] -d [-b<N>] -p [profile options]] file [args] | |
|
328 | %run [-n -i -t [-N<N>] -d [-b<N>] -p [profile options] -G] file [args] | |
|
328 | 329 | |
|
329 | 330 | Parameters after the filename are passed as command-line arguments to |
|
330 | 331 | the program (put in sys.argv). Then, control returns to IPython's |
@@ -345,6 +346,13 b' python-profiler package from non-free.""")' | |||
|
345 | 346 | and sys.argv). This allows for very convenient loading of code for |
|
346 | 347 | interactive work, while giving each program a 'clean sheet' to run in. |
|
347 | 348 | |
|
349 | Arguments are expanded using shell-like glob match. Patterns | |
|
350 | '*', '?', '[seq]' and '[!seq]' can be used. Additionally, | |
|
351 | tilde '~' will be expanded into user's home directory. Unlike | |
|
352 | real shells, quotation does not suppress expansions. Use | |
|
353 | *two* back slashes (e.g., '\\\\*') to suppress expansions. | |
|
354 | To completely disable these expansions, you can use -G flag. | |
|
355 | ||
|
348 | 356 | Options: |
|
349 | 357 | |
|
350 | 358 | -n: __name__ is NOT set to '__main__', but to the running file's name |
@@ -439,10 +447,13 b' python-profiler package from non-free.""")' | |||
|
439 | 447 | |
|
440 | 448 | will run the example module. |
|
441 | 449 | |
|
450 | -G: disable shell-like glob expansion of arguments. | |
|
451 | ||
|
442 | 452 | """ |
|
443 | 453 | |
|
444 | 454 | # get arguments and set sys.argv for program to be run. |
|
445 |
opts, arg_lst = self.parse_options(parameter_s, |
|
|
455 | opts, arg_lst = self.parse_options(parameter_s, | |
|
456 | 'nidtN:b:pD:l:rs:T:em:G', | |
|
446 | 457 | mode='list', list_all=1) |
|
447 | 458 | if "m" in opts: |
|
448 | 459 | modulename = opts["m"][0] |
@@ -476,8 +487,11 b' python-profiler package from non-free.""")' | |||
|
476 | 487 | # were run from a system shell. |
|
477 | 488 | save_argv = sys.argv # save it for later restoring |
|
478 | 489 | |
|
479 | # simulate shell expansion on arguments, at least tilde expansion | |
|
480 | args = [ os.path.expanduser(a) for a in arg_lst[1:] ] | |
|
490 | if 'G' in opts: | |
|
491 | args = arg_lst[1:] | |
|
492 | else: | |
|
493 | # tilde and glob expansion | |
|
494 | args = shellglob(map(os.path.expanduser, arg_lst[1:])) | |
|
481 | 495 | |
|
482 | 496 | sys.argv = [filename] + args # put in the proper filename |
|
483 | 497 | # protect sys.argv from potential unicode strings on Python 2: |
@@ -549,11 +563,18 b' python-profiler package from non-free.""")' | |||
|
549 | 563 | return |
|
550 | 564 | # if we find a good linenumber, set the breakpoint |
|
551 | 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 | ||
|
552 | 571 | # Start file run |
|
553 | 572 | print "NOTE: Enter 'c' at the", |
|
554 | 573 | print "%s prompt to start your script." % deb.prompt |
|
555 | 574 | ns = {'execfile': py3compat.execfile, 'prog_ns': prog_ns} |
|
556 | 575 | try: |
|
576 | #save filename so it can be used by methods on the deb object | |
|
577 | deb._exec_filename = filename | |
|
557 | 578 | deb.run('execfile("%s", prog_ns)' % filename, ns) |
|
558 | 579 | |
|
559 | 580 | except: |
@@ -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 |
@@ -23,6 +23,7 b' from IPython.core.error import StdinNotImplementedError, UsageError' | |||
|
23 | 23 | from IPython.core.magic import Magics, magics_class, line_magic |
|
24 | 24 | from IPython.testing.skipdoctest import skip_doctest |
|
25 | 25 | from IPython.utils.encoding import DEFAULT_ENCODING |
|
26 | from IPython.utils.openpy import read_py_file | |
|
26 | 27 | from IPython.utils.path import get_py_filename |
|
27 | 28 | |
|
28 | 29 | #----------------------------------------------------------------------------- |
@@ -68,7 +69,7 b' class NamespaceMagics(Magics):' | |||
|
68 | 69 | @skip_doctest |
|
69 | 70 | @line_magic |
|
70 | 71 | def pdef(self, parameter_s='', namespaces=None): |
|
71 |
"""Print the |
|
|
72 | """Print the call signature for any callable object. | |
|
72 | 73 | |
|
73 | 74 | If the object is a class, print the constructor information. |
|
74 | 75 | |
@@ -97,7 +98,7 b' class NamespaceMagics(Magics):' | |||
|
97 | 98 | self.shell._inspect('psource',parameter_s, namespaces) |
|
98 | 99 | |
|
99 | 100 | @line_magic |
|
100 | def pfile(self, parameter_s=''): | |
|
101 | def pfile(self, parameter_s='', namespaces=None): | |
|
101 | 102 | """Print (or run through pager) the file where an object is defined. |
|
102 | 103 | |
|
103 | 104 | The file opens at the line where the object definition begins. IPython |
@@ -110,7 +111,7 b' class NamespaceMagics(Magics):' | |||
|
110 | 111 | viewer.""" |
|
111 | 112 | |
|
112 | 113 | # first interpret argument as an object name |
|
113 | out = self.shell._inspect('pfile',parameter_s) | |
|
114 | out = self.shell._inspect('pfile',parameter_s, namespaces) | |
|
114 | 115 | # if not, try the input as a filename |
|
115 | 116 | if out == 'not found': |
|
116 | 117 | try: |
@@ -118,7 +119,7 b' class NamespaceMagics(Magics):' | |||
|
118 | 119 | except IOError as msg: |
|
119 | 120 | print msg |
|
120 | 121 | return |
|
121 |
page.page(self.shell. |
|
|
122 | page.page(self.shell.pycolorize(read_py_file(filename, skip_encoding_cookie=False))) | |
|
122 | 123 | |
|
123 | 124 | @line_magic |
|
124 | 125 | def psearch(self, parameter_s=''): |
@@ -32,6 +32,7 b' from IPython.core.magic import (' | |||
|
32 | 32 | ) |
|
33 | 33 | from IPython.testing.skipdoctest import skip_doctest |
|
34 | 34 | from IPython.utils.io import file_read, nlprint |
|
35 | from IPython.utils.openpy import source_to_unicode | |
|
35 | 36 | from IPython.utils.path import get_py_filename, unquote_filename |
|
36 | 37 | from IPython.utils.process import abbrev_cwd |
|
37 | 38 | from IPython.utils.terminal import set_term_title |
@@ -686,12 +687,12 b' class OSMagics(Magics):' | |||
|
686 | 687 | 'or macro.') |
|
687 | 688 | |
|
688 | 689 | try : |
|
689 | cont = self.shell.find_user_code(parameter_s) | |
|
690 | cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False) | |
|
690 | 691 | except (ValueError, IOError): |
|
691 | 692 | print "Error: no such file, variable, URL, history range or macro" |
|
692 | 693 | return |
|
693 | 694 | |
|
694 | page.page(self.shell.pycolorize(cont)) | |
|
695 | page.page(self.shell.pycolorize(source_to_unicode(cont))) | |
|
695 | 696 | |
|
696 | 697 | @magic_arguments.magic_arguments() |
|
697 | 698 | @magic_arguments.argument( |
@@ -12,6 +12,7 b'' | |||
|
12 | 12 | #----------------------------------------------------------------------------- |
|
13 | 13 | |
|
14 | 14 | # Stdlib |
|
15 | import errno | |
|
15 | 16 | import os |
|
16 | 17 | import re |
|
17 | 18 | import sys |
@@ -30,7 +31,7 b' from IPython.core.magic import (' | |||
|
30 | 31 | from IPython.lib.backgroundjobs import BackgroundJobManager |
|
31 | 32 | from IPython.testing.skipdoctest import skip_doctest |
|
32 | 33 | from IPython.utils import py3compat |
|
33 |
from IPython.utils.process import |
|
|
34 | from IPython.utils.process import arg_split | |
|
34 | 35 | from IPython.utils.traitlets import List, Dict |
|
35 | 36 | |
|
36 | 37 | #----------------------------------------------------------------------------- |
@@ -90,36 +91,23 b' class ScriptMagics(Magics, Configurable):' | |||
|
90 | 91 | """, |
|
91 | 92 | ) |
|
92 | 93 | def _script_magics_default(self): |
|
93 |
"""default to a common list of programs |
|
|
94 | """default to a common list of programs""" | |
|
94 | 95 | |
|
95 |
defaults = [ |
|
|
96 | to_try = [] | |
|
97 | if os.name == 'nt': | |
|
98 | defaults.append('cmd') | |
|
99 | to_try.append('powershell') | |
|
100 | to_try.extend([ | |
|
96 | defaults = [ | |
|
101 | 97 | 'sh', |
|
102 | 98 | 'bash', |
|
103 | 99 | 'perl', |
|
104 | 100 | 'ruby', |
|
101 | 'python', | |
|
105 | 102 | 'python3', |
|
106 | 103 | 'pypy', |
|
107 |
] |
|
|
104 | ] | |
|
105 | if os.name == 'nt': | |
|
106 | defaults.extend([ | |
|
107 | 'cmd', | |
|
108 | 'powershell', | |
|
109 | ]) | |
|
108 | 110 | |
|
109 | for cmd in to_try: | |
|
110 | if cmd in self.script_paths: | |
|
111 | defaults.append(cmd) | |
|
112 | else: | |
|
113 | try: | |
|
114 | find_cmd(cmd) | |
|
115 | except FindCmdError: | |
|
116 | # command not found, ignore it | |
|
117 | pass | |
|
118 | except ImportError: | |
|
119 | # Windows without pywin32, find_cmd doesn't work | |
|
120 | pass | |
|
121 | else: | |
|
122 | defaults.append(cmd) | |
|
123 | 111 | return defaults |
|
124 | 112 | |
|
125 | 113 | script_paths = Dict(config=True, |
@@ -197,8 +185,15 b' class ScriptMagics(Magics, Configurable):' | |||
|
197 | 185 | """ |
|
198 | 186 | argv = arg_split(line, posix = not sys.platform.startswith('win')) |
|
199 | 187 | args, cmd = self.shebang.parser.parse_known_args(argv) |
|
200 | ||
|
201 | p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE) | |
|
188 | ||
|
189 | try: | |
|
190 | p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE) | |
|
191 | except OSError as e: | |
|
192 | if e.errno == errno.ENOENT: | |
|
193 | print "Couldn't find program: %r" % cmd[0] | |
|
194 | return | |
|
195 | else: | |
|
196 | raise | |
|
202 | 197 | |
|
203 | 198 | cell = cell.encode('utf8', 'replace') |
|
204 | 199 | if args.bg: |
@@ -24,6 +24,8 b' import linecache' | |||
|
24 | 24 | import os |
|
25 | 25 | import sys |
|
26 | 26 | import types |
|
27 | import io as stdlib_io | |
|
28 | ||
|
27 | 29 | from collections import namedtuple |
|
28 | 30 | try: |
|
29 | 31 | from itertools import izip_longest |
@@ -35,10 +37,12 b' from IPython.core import page' | |||
|
35 | 37 | from IPython.testing.skipdoctest import skip_doctest_py3 |
|
36 | 38 | from IPython.utils import PyColorize |
|
37 | 39 | from IPython.utils import io |
|
40 | from IPython.utils import openpy | |
|
38 | 41 | from IPython.utils import py3compat |
|
39 | 42 | from IPython.utils.text import indent |
|
40 | 43 | from IPython.utils.wildcard import list_namespace |
|
41 | 44 | from IPython.utils.coloransi import * |
|
45 | from IPython.utils.py3compat import cast_unicode | |
|
42 | 46 | |
|
43 | 47 | #**************************************************************************** |
|
44 | 48 | # Builtin color schemes |
@@ -90,6 +94,29 b' def object_info(**kw):' | |||
|
90 | 94 | return infodict |
|
91 | 95 | |
|
92 | 96 | |
|
97 | def get_encoding(obj): | |
|
98 | """Get encoding for python source file defining obj | |
|
99 | ||
|
100 | Returns None if obj is not defined in a sourcefile. | |
|
101 | """ | |
|
102 | ofile = find_file(obj) | |
|
103 | # run contents of file through pager starting at line where the object | |
|
104 | # is defined, as long as the file isn't binary and is actually on the | |
|
105 | # filesystem. | |
|
106 | if ofile is None: | |
|
107 | return None | |
|
108 | elif ofile.endswith(('.so', '.dll', '.pyd')): | |
|
109 | return None | |
|
110 | elif not os.path.isfile(ofile): | |
|
111 | return None | |
|
112 | else: | |
|
113 | # Print only text files, not extension binaries. Note that | |
|
114 | # getsourcelines returns lineno with 1-offset and page() uses | |
|
115 | # 0-offset, so we must adjust. | |
|
116 | buffer = stdlib_io.open(ofile, 'rb') # Tweaked to use io.open for Python 2 | |
|
117 | encoding, lines = openpy.detect_encoding(buffer.readline) | |
|
118 | return encoding | |
|
119 | ||
|
93 | 120 | def getdoc(obj): |
|
94 | 121 | """Stable wrapper around inspect.getdoc. |
|
95 | 122 | |
@@ -109,10 +136,13 b' def getdoc(obj):' | |||
|
109 | 136 | return inspect.cleandoc(ds) |
|
110 | 137 | |
|
111 | 138 | try: |
|
112 |
|
|
|
139 | docstr = inspect.getdoc(obj) | |
|
140 | encoding = get_encoding(obj) | |
|
141 | return py3compat.cast_unicode(docstr, encoding=encoding) | |
|
113 | 142 | except Exception: |
|
114 | 143 | # Harden against an inspect failure, which can occur with |
|
115 | 144 | # SWIG-wrapped extensions. |
|
145 | raise | |
|
116 | 146 | return None |
|
117 | 147 | |
|
118 | 148 | |
@@ -143,7 +173,8 b' def getsource(obj,is_binary=False):' | |||
|
143 | 173 | except TypeError: |
|
144 | 174 | if hasattr(obj,'__class__'): |
|
145 | 175 | src = inspect.getsource(obj.__class__) |
|
146 | return src | |
|
176 | encoding = get_encoding(obj) | |
|
177 | return cast_unicode(src, encoding=encoding) | |
|
147 | 178 | |
|
148 | 179 | def getargspec(obj): |
|
149 | 180 | """Get the names and default values of a function's arguments. |
@@ -313,15 +344,14 b' class Inspector:' | |||
|
313 | 344 | self.set_active_scheme(scheme) |
|
314 | 345 | |
|
315 | 346 | def _getdef(self,obj,oname=''): |
|
316 |
"""Return the |
|
|
347 | """Return the call signature for any callable object. | |
|
317 | 348 | |
|
318 | 349 | If any exception is generated, None is returned instead and the |
|
319 | 350 | exception is suppressed.""" |
|
320 | 351 | |
|
321 | 352 | try: |
|
322 | # We need a plain string here, NOT unicode! | |
|
323 | 353 | hdef = oname + inspect.formatargspec(*getargspec(obj)) |
|
324 |
return |
|
|
354 | return cast_unicode(hdef) | |
|
325 | 355 | except: |
|
326 | 356 | return None |
|
327 | 357 | |
@@ -343,7 +373,7 b' class Inspector:' | |||
|
343 | 373 | print() |
|
344 | 374 | |
|
345 | 375 | def pdef(self, obj, oname=''): |
|
346 |
"""Print the |
|
|
376 | """Print the call signature for any callable object. | |
|
347 | 377 | |
|
348 | 378 | If the object is a class, print the constructor information.""" |
|
349 | 379 | |
@@ -435,7 +465,7 b' class Inspector:' | |||
|
435 | 465 | except: |
|
436 | 466 | self.noinfo('source',oname) |
|
437 | 467 | else: |
|
438 |
page.page(self.format( |
|
|
468 | page.page(self.format(src)) | |
|
439 | 469 | |
|
440 | 470 | def pfile(self, obj, oname=''): |
|
441 | 471 | """Show the whole file where an object was defined.""" |
@@ -457,7 +487,7 b' class Inspector:' | |||
|
457 | 487 | # Print only text files, not extension binaries. Note that |
|
458 | 488 | # getsourcelines returns lineno with 1-offset and page() uses |
|
459 | 489 | # 0-offset, so we must adjust. |
|
460 |
page.page(self.format(open(ofile |
|
|
490 | page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1) | |
|
461 | 491 | |
|
462 | 492 | def _format_fields(self, fields, title_width=12): |
|
463 | 493 | """Formats a list of fields for display. |
@@ -476,7 +506,7 b' class Inspector:' | |||
|
476 | 506 | title = header(title + ":") + "\n" |
|
477 | 507 | else: |
|
478 | 508 | title = header((title+":").ljust(title_width)) |
|
479 | out.append(title + content) | |
|
509 | out.append(cast_unicode(title) + cast_unicode(content)) | |
|
480 | 510 | return "\n".join(out) |
|
481 | 511 | |
|
482 | 512 | # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict) |
@@ -536,7 +566,8 b' class Inspector:' | |||
|
536 | 566 | # Source or docstring, depending on detail level and whether |
|
537 | 567 | # source found. |
|
538 | 568 | if detail_level > 0 and info['source'] is not None: |
|
539 |
displayfields.append(("Source", |
|
|
569 | displayfields.append(("Source", | |
|
570 | self.format(cast_unicode(info['source'])))) | |
|
540 | 571 | elif info['docstring'] is not None: |
|
541 | 572 | displayfields.append(("Docstring", info["docstring"])) |
|
542 | 573 |
@@ -110,10 +110,10 b' class ProfileDir(LoggingConfigurable):' | |||
|
110 | 110 | |
|
111 | 111 | def check_security_dir(self): |
|
112 | 112 | if not os.path.isdir(self.security_dir): |
|
113 | os.mkdir(self.security_dir, 0700) | |
|
113 | os.mkdir(self.security_dir, 0o700) | |
|
114 | 114 | else: |
|
115 | 115 | try: |
|
116 | os.chmod(self.security_dir, 0700) | |
|
116 | os.chmod(self.security_dir, 0o700) | |
|
117 | 117 | except OSError: |
|
118 | 118 | self.log.warn("Could not set security dir permissions to private.") |
|
119 | 119 | |
@@ -122,10 +122,10 b' class ProfileDir(LoggingConfigurable):' | |||
|
122 | 122 | |
|
123 | 123 | def check_pid_dir(self): |
|
124 | 124 | if not os.path.isdir(self.pid_dir): |
|
125 | os.mkdir(self.pid_dir, 0700) | |
|
125 | os.mkdir(self.pid_dir, 0o700) | |
|
126 | 126 | else: |
|
127 | 127 | try: |
|
128 | os.chmod(self.pid_dir, 0700) | |
|
128 | os.chmod(self.pid_dir, 0o700) | |
|
129 | 129 | except OSError: |
|
130 | 130 | self.log.warn("Could not set pid dir permissions to private.") |
|
131 | 131 |
@@ -21,7 +21,7 b" name = 'ipython'" | |||
|
21 | 21 | # version |
|
22 | 22 | _version_major = 0 |
|
23 | 23 | _version_minor = 14 |
|
24 |
_version_micro = |
|
|
24 | _version_micro = 0 # use 0 for first of series, number for 1 and above | |
|
25 | 25 | _version_extra = 'dev' |
|
26 | 26 | #_version_extra = 'rc1' |
|
27 | 27 | # _version_extra = '' # Uncomment this for full releases |
@@ -36,6 +36,7 b' if _version_extra:' | |||
|
36 | 36 | __version__ = '.'.join(map(str, _ver)) |
|
37 | 37 | |
|
38 | 38 | version = __version__ # backwards compatibility name |
|
39 | version_info = (_version_major, _version_minor, _version_micro, _version_extra) | |
|
39 | 40 | |
|
40 | 41 | description = "IPython: Productive Interactive Computing" |
|
41 | 42 |
@@ -277,12 +277,20 b' class InteractiveShellApp(Configurable):' | |||
|
277 | 277 | sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ] |
|
278 | 278 | try: |
|
279 | 279 | if os.path.isfile(full_filename): |
|
280 | self.log.info("Running file in user namespace: %s" % full_filename) | |
|
281 | 280 | if full_filename.endswith('.ipy'): |
|
281 | self.log.info("Running file in user namespace: %s" % | |
|
282 | full_filename) | |
|
282 | 283 | self.shell.safe_execfile_ipy(full_filename) |
|
283 | 284 | else: |
|
284 | 285 | # default to python, even without extension |
|
285 | self.shell.safe_execfile(full_filename, self.shell.user_ns) | |
|
286 | self.log.info("Running file in user namespace: %s" % | |
|
287 | full_filename) | |
|
288 | # Ensure that __file__ is always defined to match Python behavior | |
|
289 | self.shell.user_ns['__file__'] = fname | |
|
290 | try: | |
|
291 | self.shell.safe_execfile(full_filename, self.shell.user_ns) | |
|
292 | finally: | |
|
293 | del self.shell.user_ns['__file__'] | |
|
286 | 294 | finally: |
|
287 | 295 | sys.argv = save_argv |
|
288 | 296 |
@@ -87,6 +87,18 b' def test_history():' | |||
|
87 | 87 | # Check get_hist_search |
|
88 | 88 | gothist = ip.history_manager.search("*test*") |
|
89 | 89 | nt.assert_equal(list(gothist), [(1,2,hist[1])] ) |
|
90 | gothist = ip.history_manager.search("*=*") | |
|
91 | nt.assert_equal(list(gothist), | |
|
92 | [(1, 1, hist[0]), | |
|
93 | (1, 2, hist[1]), | |
|
94 | (1, 3, hist[2]), | |
|
95 | (2, 1, newcmds[0]), | |
|
96 | (2, 3, newcmds[2])]) | |
|
97 | gothist = ip.history_manager.search("*=*", n=3) | |
|
98 | nt.assert_equal(list(gothist), | |
|
99 | [(1, 3, hist[2]), | |
|
100 | (2, 1, newcmds[0]), | |
|
101 | (2, 3, newcmds[2])]) | |
|
90 | 102 | gothist = ip.history_manager.search("b*", output=True) |
|
91 | 103 | nt.assert_equal(list(gothist), [(1,3,(hist[2],"spam"))] ) |
|
92 | 104 |
@@ -86,6 +86,56 b' def doctest_run_builtins():' | |||
|
86 | 86 | ....: |
|
87 | 87 | """ |
|
88 | 88 | |
|
89 | ||
|
90 | def doctest_run_option_parser(): | |
|
91 | r"""Test option parser in %run. | |
|
92 | ||
|
93 | In [1]: %run print_argv.py | |
|
94 | [] | |
|
95 | ||
|
96 | In [2]: %run print_argv.py print*.py | |
|
97 | ['print_argv.py'] | |
|
98 | ||
|
99 | In [3]: %run -G print_argv.py print*.py | |
|
100 | ['print*.py'] | |
|
101 | ||
|
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' | |
|
117 | ['print_argv.py'] | |
|
118 | ||
|
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' | |
|
134 | ['print*.py'] | |
|
135 | ||
|
136 | """ | |
|
137 | ||
|
138 | ||
|
89 | 139 | @py3compat.doctest_refactor_print |
|
90 | 140 | def doctest_reset_del(): |
|
91 | 141 | """Test that resetting doesn't cause errors in __del__ methods. |
@@ -1,6 +1,7 b'' | |||
|
1 | # encoding: utf-8 | |
|
1 | 2 | """Tests for IPython.core.ultratb |
|
2 | 3 | """ |
|
3 | ||
|
4 | import io | |
|
4 | 5 | import os.path |
|
5 | 6 | import unittest |
|
6 | 7 | |
@@ -49,3 +50,52 b' class ChangedPyFileTest(unittest.TestCase):' | |||
|
49 | 50 | ip.run_cell("foo.f()") |
|
50 | 51 | with tt.AssertPrints("ZeroDivisionError"): |
|
51 | 52 | ip.run_cell("foo.f()") |
|
53 | ||
|
54 | iso_8859_5_file = u'''# coding: iso-8859-5 | |
|
55 | ||
|
56 | def fail(): | |
|
57 | """дбИЖ""" | |
|
58 | 1/0 # дбИЖ | |
|
59 | ''' | |
|
60 | ||
|
61 | class NonAsciiTest(unittest.TestCase): | |
|
62 | def test_iso8859_5(self): | |
|
63 | # Non-ascii directory name as well. | |
|
64 | with TemporaryDirectory(suffix=u'é') as td: | |
|
65 | fname = os.path.join(td, 'dfghjkl.py') | |
|
66 | ||
|
67 | with io.open(fname, 'w', encoding='iso-8859-5') as f: | |
|
68 | f.write(iso_8859_5_file) | |
|
69 | ||
|
70 | with prepended_to_syspath(td): | |
|
71 | ip.run_cell("from dfghjkl import fail") | |
|
72 | ||
|
73 | with tt.AssertPrints("ZeroDivisionError"): | |
|
74 | with tt.AssertPrints(u'дбИЖ', suppress=False): | |
|
75 | ip.run_cell('fail()') | |
|
76 | ||
|
77 | indentationerror_file = """if True: | |
|
78 | zoon() | |
|
79 | """ | |
|
80 | ||
|
81 | class IndentationErrorTest(unittest.TestCase): | |
|
82 | def test_indentationerror_shows_line(self): | |
|
83 | # See issue gh-2398 | |
|
84 | with tt.AssertPrints("IndentationError"): | |
|
85 | with tt.AssertPrints("zoon()", suppress=False): | |
|
86 | ip.run_cell(indentationerror_file) | |
|
87 | ||
|
88 | with TemporaryDirectory() as td: | |
|
89 | fname = os.path.join(td, "foo.py") | |
|
90 | with open(fname, "w") as f: | |
|
91 | f.write(indentationerror_file) | |
|
92 | ||
|
93 | with tt.AssertPrints("IndentationError"): | |
|
94 | with tt.AssertPrints("zoon()", suppress=False): | |
|
95 | ip.magic('run %s' % fname) | |
|
96 | ||
|
97 | class SyntaxErrorTest(unittest.TestCase): | |
|
98 | def test_syntaxerror_without_lineno(self): | |
|
99 | with tt.AssertNotPrints("TypeError"): | |
|
100 | with tt.AssertPrints("line unknown"): | |
|
101 | ip.run_cell("raise SyntaxError()") |
@@ -69,7 +69,7 b' possible inclusion in future releases.' | |||
|
69 | 69 | # the file COPYING, distributed as part of this software. |
|
70 | 70 | #***************************************************************************** |
|
71 | 71 | |
|
72 |
from __future__ import |
|
|
72 | from __future__ import unicode_literals | |
|
73 | 73 | |
|
74 | 74 | import inspect |
|
75 | 75 | import keyword |
@@ -99,9 +99,12 b' from IPython.core.display_trap import DisplayTrap' | |||
|
99 | 99 | from IPython.core.excolors import exception_colors |
|
100 | 100 | from IPython.utils import PyColorize |
|
101 | 101 | from IPython.utils import io |
|
102 | from IPython.utils import path as util_path | |
|
102 | 103 | from IPython.utils import py3compat |
|
103 | 104 | from IPython.utils import pyfile |
|
105 | from IPython.utils import ulinecache | |
|
104 | 106 | from IPython.utils.data import uniq_stable |
|
107 | from IPython.utils.openpy import read_py_file | |
|
105 | 108 | from IPython.utils.warn import info, error |
|
106 | 109 | |
|
107 | 110 | # Globals |
@@ -229,7 +232,6 b' def fix_frame_records_filenames(records):' | |||
|
229 | 232 | |
|
230 | 233 | |
|
231 | 234 | def _fixed_getinnerframes(etb, context=1,tb_offset=0): |
|
232 | import linecache | |
|
233 | 235 | LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5 |
|
234 | 236 | |
|
235 | 237 | records = fix_frame_records_filenames(inspect.getinnerframes(etb, context)) |
@@ -251,7 +253,7 b' def _fixed_getinnerframes(etb, context=1,tb_offset=0):' | |||
|
251 | 253 | maybeStart = lnum-1 - context//2 |
|
252 | 254 | start = max(maybeStart, 0) |
|
253 | 255 | end = start + context |
|
254 | lines = linecache.getlines(file)[start:end] | |
|
256 | lines = ulinecache.getlines(file)[start:end] | |
|
255 | 257 | buf = list(records[i]) |
|
256 | 258 | buf[LNUM_POS] = lnum |
|
257 | 259 | buf[INDEX_POS] = lnum - 1 - start |
@@ -282,12 +284,7 b' def _format_traceback_lines(lnum, index, lines, Colors, lvals=None,scheme=None):' | |||
|
282 | 284 | _line_format = _parser.format2 |
|
283 | 285 | |
|
284 | 286 | for line in lines: |
|
285 | # FIXME: we need to ensure the source is a pure string at this point, | |
|
286 | # else the coloring code makes a royal mess. This is in need of a | |
|
287 | # serious refactoring, so that all of the ultratb and PyColorize code | |
|
288 | # is unicode-safe. So for now this is rather an ugly hack, but | |
|
289 | # necessary to at least have readable tracebacks. Improvements welcome! | |
|
290 | line = py3compat.cast_bytes_py2(line, 'utf-8') | |
|
287 | line = py3compat.cast_unicode(line) | |
|
291 | 288 | |
|
292 | 289 | new_line, err = _line_format(line, 'str', scheme) |
|
293 | 290 | if not err: line = new_line |
@@ -547,33 +544,41 b' class ListTB(TBTools):' | |||
|
547 | 544 | |
|
548 | 545 | Also lifted nearly verbatim from traceback.py |
|
549 | 546 | """ |
|
550 | ||
|
551 | 547 | have_filedata = False |
|
552 | 548 | Colors = self.Colors |
|
553 | 549 | list = [] |
|
554 | 550 | stype = Colors.excName + etype.__name__ + Colors.Normal |
|
555 | 551 | if value is None: |
|
556 | 552 | # Not sure if this can still happen in Python 2.6 and above |
|
557 |
list.append( |
|
|
553 | list.append( py3compat.cast_unicode(stype) + '\n') | |
|
558 | 554 | else: |
|
559 |
if etype |
|
|
555 | if issubclass(etype, SyntaxError): | |
|
560 | 556 | have_filedata = True |
|
561 | 557 | #print 'filename is',filename # dbg |
|
562 | 558 | if not value.filename: value.filename = "<string>" |
|
563 | list.append('%s File %s"%s"%s, line %s%d%s\n' % \ | |
|
559 | if value.lineno: | |
|
560 | lineno = value.lineno | |
|
561 | textline = ulinecache.getline(value.filename, value.lineno) | |
|
562 | else: | |
|
563 | lineno = 'unknown' | |
|
564 | textline = '' | |
|
565 | list.append('%s File %s"%s"%s, line %s%s%s\n' % \ | |
|
564 | 566 | (Colors.normalEm, |
|
565 | Colors.filenameEm, value.filename, Colors.normalEm, | |
|
566 |
Colors.linenoEm, |
|
|
567 |
if |
|
|
567 | Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm, | |
|
568 | Colors.linenoEm, lineno, Colors.Normal )) | |
|
569 | if textline == '': | |
|
570 | textline = py3compat.cast_unicode(value.text, "utf-8") | |
|
571 | ||
|
572 | if textline is not None: | |
|
568 | 573 | i = 0 |
|
569 |
while i < len( |
|
|
574 | while i < len(textline) and textline[i].isspace(): | |
|
570 | 575 | i += 1 |
|
571 | 576 | list.append('%s %s%s\n' % (Colors.line, |
|
572 |
|
|
|
577 | textline.strip(), | |
|
573 | 578 | Colors.Normal)) |
|
574 | 579 | if value.offset is not None: |
|
575 | 580 | s = ' ' |
|
576 |
for c in |
|
|
581 | for c in textline[i:value.offset-1]: | |
|
577 | 582 | if c.isspace(): |
|
578 | 583 | s += c |
|
579 | 584 | else: |
@@ -779,10 +784,9 b' class VerboseTB(TBTools):' | |||
|
779 | 784 | abspath = os.path.abspath |
|
780 | 785 | for frame, file, lnum, func, lines, index in records: |
|
781 | 786 | #print '*** record:',file,lnum,func,lines,index # dbg |
|
782 | ||
|
783 | 787 | if not file: |
|
784 | 788 | file = '?' |
|
785 | elif not(file.startswith("<") and file.endswith(">")): | |
|
789 | elif not(file.startswith(str("<")) and file.endswith(str(">"))): | |
|
786 | 790 | # Guess that filenames like <string> aren't real filenames, so |
|
787 | 791 | # don't call abspath on them. |
|
788 | 792 | try: |
@@ -791,7 +795,7 b' class VerboseTB(TBTools):' | |||
|
791 | 795 | # Not sure if this can still happen: abspath now works with |
|
792 | 796 | # file names like <string> |
|
793 | 797 | pass |
|
794 | ||
|
798 | file = py3compat.cast_unicode(file, util_path.fs_encoding) | |
|
795 | 799 | link = tpl_link % file |
|
796 | 800 | args, varargs, varkw, locals = inspect.getargvalues(frame) |
|
797 | 801 | |
@@ -831,7 +835,7 b' class VerboseTB(TBTools):' | |||
|
831 | 835 | # Look up the corresponding source file. |
|
832 | 836 | file = pyfile.source_from_cache(file) |
|
833 | 837 | |
|
834 | def linereader(file=file, lnum=[lnum], getline=linecache.getline): | |
|
838 | def linereader(file=file, lnum=[lnum], getline=ulinecache.getline): | |
|
835 | 839 | line = getline(file, lnum[0]) |
|
836 | 840 | lnum[0] += 1 |
|
837 | 841 | return line |
@@ -926,7 +930,7 b' class VerboseTB(TBTools):' | |||
|
926 | 930 | etype_str,evalue_str = map(str,(etype,evalue)) |
|
927 | 931 | # ... and format it |
|
928 | 932 | exception = ['%s%s%s: %s' % (Colors.excName, etype_str, |
|
929 | ColorsNormal, evalue_str)] | |
|
933 | ColorsNormal, py3compat.cast_unicode(evalue_str))] | |
|
930 | 934 | if (not py3compat.PY3) and type(evalue) is types.InstanceType: |
|
931 | 935 | try: |
|
932 | 936 | names = [w for w in dir(evalue) if isinstance(w, basestring)] |
@@ -938,7 +942,7 b' class VerboseTB(TBTools):' | |||
|
938 | 942 | exception.append(_m % (Colors.excName,ColorsNormal)) |
|
939 | 943 | etype_str,evalue_str = map(str,sys.exc_info()[:2]) |
|
940 | 944 | exception.append('%s%s%s: %s' % (Colors.excName,etype_str, |
|
941 | ColorsNormal, evalue_str)) | |
|
945 | ColorsNormal, py3compat.cast_unicode(evalue_str))) | |
|
942 | 946 | names = [] |
|
943 | 947 | for name in names: |
|
944 | 948 | value = text_repr(getattr(evalue, name)) |
@@ -106,13 +106,10 b' skip_doctest = True' | |||
|
106 | 106 | #----------------------------------------------------------------------------- |
|
107 | 107 | # Imports |
|
108 | 108 | #----------------------------------------------------------------------------- |
|
109 | import atexit | |
|
109 | ||
|
110 | 110 | import imp |
|
111 | import inspect | |
|
112 | 111 | import os |
|
113 | 112 | import sys |
|
114 | import threading | |
|
115 | import time | |
|
116 | 113 | import traceback |
|
117 | 114 | import types |
|
118 | 115 | import weakref |
@@ -409,7 +406,6 b' def superreload(module, reload=reload, old_objects={}):' | |||
|
409 | 406 | |
|
410 | 407 | from IPython.core.hooks import TryNext |
|
411 | 408 | from IPython.core.magic import Magics, magics_class, line_magic |
|
412 | from IPython.core.plugin import Plugin | |
|
413 | 409 | |
|
414 | 410 | @magics_class |
|
415 | 411 | class AutoreloadMagics(Magics): |
@@ -518,21 +514,8 b' class AutoreloadMagics(Magics):' | |||
|
518 | 514 | pass |
|
519 | 515 | |
|
520 | 516 | |
|
521 | class AutoreloadPlugin(Plugin): | |
|
522 | def __init__(self, shell=None, config=None): | |
|
523 | super(AutoreloadPlugin, self).__init__(shell=shell, config=config) | |
|
524 | self.auto_magics = AutoreloadMagics(shell) | |
|
525 | shell.register_magics(self.auto_magics) | |
|
526 | shell.set_hook('pre_run_code_hook', self.auto_magics.pre_run_code_hook) | |
|
527 | ||
|
528 | ||
|
529 | _loaded = False | |
|
530 | ||
|
531 | ||
|
532 | 517 | def load_ipython_extension(ip): |
|
533 | 518 | """Load the extension in IPython.""" |
|
534 | global _loaded | |
|
535 | if not _loaded: | |
|
536 | plugin = AutoreloadPlugin(shell=ip, config=ip.config) | |
|
537 | ip.plugin_manager.register_plugin('autoreload', plugin) | |
|
538 | _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) |
@@ -125,6 +125,11 b' class CythonMagics(Magics):' | |||
|
125 | 125 | "multiple times)." |
|
126 | 126 | ) |
|
127 | 127 | @magic_arguments.argument( |
|
128 | '-L', dest='library_dirs', metavar='dir', action='append', default=[], | |
|
129 | help="Add a path to the list of libary directories (can be specified " | |
|
130 | "multiple times)." | |
|
131 | ) | |
|
132 | @magic_arguments.argument( | |
|
128 | 133 | '-I', '--include', action='append', default=[], |
|
129 | 134 | help="Add a path to the list of include directories (can be specified " |
|
130 | 135 | "multiple times)." |
@@ -195,6 +200,7 b' class CythonMagics(Magics):' | |||
|
195 | 200 | name = module_name, |
|
196 | 201 | sources = [pyx_file], |
|
197 | 202 | include_dirs = c_include_dirs, |
|
203 | library_dirs = args.library_dirs, | |
|
198 | 204 | extra_compile_args = args.compile_args, |
|
199 | 205 | extra_link_args = args.link_args, |
|
200 | 206 | libraries = args.lib, |
@@ -267,11 +273,7 b' class CythonMagics(Magics):' | |||
|
267 | 273 | html = '\n'.join(l for l in html.splitlines() if not r.match(l)) |
|
268 | 274 | return html |
|
269 | 275 | |
|
270 | _loaded = False | |
|
271 | 276 | |
|
272 | 277 | def load_ipython_extension(ip): |
|
273 | 278 | """Load the extension in IPython.""" |
|
274 | global _loaded | |
|
275 | if not _loaded: | |
|
276 | ip.register_magics(CythonMagics) | |
|
277 | _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) |
@@ -28,9 +28,7 b' import inspect, os, sys, textwrap' | |||
|
28 | 28 | from IPython.core.error import UsageError |
|
29 | 29 | from IPython.core.fakemodule import FakeModule |
|
30 | 30 | from IPython.core.magic import Magics, magics_class, line_magic |
|
31 | from IPython.core.plugin import Plugin | |
|
32 | 31 | from IPython.testing.skipdoctest import skip_doctest |
|
33 | from IPython.utils.traitlets import Bool, Instance | |
|
34 | 32 | |
|
35 | 33 | #----------------------------------------------------------------------------- |
|
36 | 34 | # Functions and classes |
@@ -211,24 +209,6 b' class StoreMagics(Magics):' | |||
|
211 | 209 | print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__) |
|
212 | 210 | |
|
213 | 211 | |
|
214 | class StoreMagic(Plugin): | |
|
215 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') | |
|
216 | autorestore = Bool(False, config=True) | |
|
217 | ||
|
218 | def __init__(self, shell, config): | |
|
219 | super(StoreMagic, self).__init__(shell=shell, config=config) | |
|
220 | shell.register_magics(StoreMagics) | |
|
221 | ||
|
222 | if self.autorestore: | |
|
223 | restore_data(shell) | |
|
224 | ||
|
225 | ||
|
226 | _loaded = False | |
|
227 | ||
|
228 | 212 | def load_ipython_extension(ip): |
|
229 | 213 | """Load the extension in IPython.""" |
|
230 | global _loaded | |
|
231 | if not _loaded: | |
|
232 | plugin = StoreMagic(shell=ip, config=ip.config) | |
|
233 | ip.plugin_manager.register_plugin('storemagic', plugin) | |
|
234 | _loaded = True | |
|
214 | ip.register_magics(StoreMagics) |
@@ -9,6 +9,11 b' Usage' | |||
|
9 | 9 | Once the extension is loaded, Sympy Basic objects are automatically |
|
10 | 10 | pretty-printed. |
|
11 | 11 | |
|
12 | As of SymPy 0.7.2, maintenance of this extension has moved to SymPy under | |
|
13 | sympy.interactive.ipythonprinting, any modifications to account for changes to | |
|
14 | SymPy should be submitted to SymPy rather than changed here. This module is | |
|
15 | maintained here for backwards compatablitiy with old SymPy versions. | |
|
16 | ||
|
12 | 17 | """ |
|
13 | 18 | #----------------------------------------------------------------------------- |
|
14 | 19 | # Copyright (C) 2008 The IPython Development Team |
@@ -30,6 +35,8 b' try:' | |||
|
30 | 35 | except ImportError: |
|
31 | 36 | pass |
|
32 | 37 | |
|
38 | import warnings | |
|
39 | ||
|
33 | 40 | #----------------------------------------------------------------------------- |
|
34 | 41 | # Definitions of special display functions for use with IPython |
|
35 | 42 | #----------------------------------------------------------------------------- |
@@ -101,6 +108,19 b' _loaded = False' | |||
|
101 | 108 | def load_ipython_extension(ip): |
|
102 | 109 | """Load the extension in IPython.""" |
|
103 | 110 | import sympy |
|
111 | ||
|
112 | # sympyprinting extension has been moved to SymPy as of 0.7.2, if it | |
|
113 | # exists there, warn the user and import it | |
|
114 | try: | |
|
115 | import sympy.interactive.ipythonprinting | |
|
116 | except ImportError: | |
|
117 | pass | |
|
118 | else: | |
|
119 | warnings.warn("The sympyprinting extension in IPython is deprecated, " | |
|
120 | "use sympy.interactive.ipythonprinting") | |
|
121 | ip.extension_manager.load_extension('sympy.interactive.ipythonprinting') | |
|
122 | return | |
|
123 | ||
|
104 | 124 | global _loaded |
|
105 | 125 | if not _loaded: |
|
106 | 126 | plaintext_formatter = ip.display_formatter.formatters['text/plain'] |
@@ -23,7 +23,7 b' from StringIO import StringIO' | |||
|
23 | 23 | import nose.tools as nt |
|
24 | 24 | import IPython.testing.tools as tt |
|
25 | 25 | |
|
26 |
from IPython.extensions.autoreload import Autoreload |
|
|
26 | from IPython.extensions.autoreload import AutoreloadMagics | |
|
27 | 27 | from IPython.core.hooks import TryNext |
|
28 | 28 | |
|
29 | 29 | #----------------------------------------------------------------------------- |
@@ -35,13 +35,13 b' noop = lambda *a, **kw: None' | |||
|
35 | 35 | class FakeShell(object): |
|
36 | 36 | def __init__(self): |
|
37 | 37 | self.ns = {} |
|
38 |
self. |
|
|
38 | self.auto_magics = AutoreloadMagics(shell=self) | |
|
39 | 39 | |
|
40 | 40 | register_magics = set_hook = noop |
|
41 | 41 | |
|
42 | 42 | def run_code(self, code): |
|
43 | 43 | try: |
|
44 |
self |
|
|
44 | self.auto_magics.pre_run_code_hook(self) | |
|
45 | 45 | except TryNext: |
|
46 | 46 | pass |
|
47 | 47 | exec code in self.ns |
@@ -50,10 +50,10 b' class FakeShell(object):' | |||
|
50 | 50 | self.ns.update(items) |
|
51 | 51 | |
|
52 | 52 | def magic_autoreload(self, parameter): |
|
53 |
self |
|
|
53 | self.auto_magics.autoreload(parameter) | |
|
54 | 54 | |
|
55 | 55 | def magic_aimport(self, parameter, stream=None): |
|
56 |
self |
|
|
56 | self.auto_magics.aimport(parameter, stream=stream) | |
|
57 | 57 | |
|
58 | 58 | |
|
59 | 59 | class Fixture(object): |
@@ -72,7 +72,6 b' class Fixture(object):' | |||
|
72 | 72 | def tearDown(self): |
|
73 | 73 | shutil.rmtree(self.test_dir) |
|
74 | 74 | sys.path = self.old_sys_path |
|
75 | self.shell.reloader.enabled = False | |
|
76 | 75 | |
|
77 | 76 | self.test_dir = None |
|
78 | 77 | self.old_sys_path = None |
@@ -860,10 +860,10 b' class path(unicode):' | |||
|
860 | 860 | |
|
861 | 861 | # --- Create/delete operations on directories |
|
862 | 862 | |
|
863 | def mkdir(self, mode=0777): | |
|
863 | def mkdir(self, mode=0o777): | |
|
864 | 864 | os.mkdir(self, mode) |
|
865 | 865 | |
|
866 | def makedirs(self, mode=0777): | |
|
866 | def makedirs(self, mode=0o777): | |
|
867 | 867 | os.makedirs(self, mode) |
|
868 | 868 | |
|
869 | 869 | def rmdir(self): |
@@ -879,7 +879,7 b' class path(unicode):' | |||
|
879 | 879 | """ Set the access/modified times of this file to the current time. |
|
880 | 880 | Create the file if it does not exist. |
|
881 | 881 | """ |
|
882 | fd = os.open(self, os.O_WRONLY | os.O_CREAT, 0666) | |
|
882 | fd = os.open(self, os.O_WRONLY | os.O_CREAT, 0o666) | |
|
883 | 883 | os.close(fd) |
|
884 | 884 | os.utime(self, None) |
|
885 | 885 |
@@ -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: |
@@ -24,6 +24,7 b' Authors:' | |||
|
24 | 24 | import atexit |
|
25 | 25 | import json |
|
26 | 26 | import os |
|
27 | import shutil | |
|
27 | 28 | import signal |
|
28 | 29 | import sys |
|
29 | 30 | import uuid |
@@ -38,7 +39,7 b' from IPython.zmq.blockingkernelmanager import BlockingKernelManager' | |||
|
38 | 39 | from IPython.utils.path import filefind |
|
39 | 40 | from IPython.utils.py3compat import str_to_bytes |
|
40 | 41 | from IPython.utils.traitlets import ( |
|
41 | Dict, List, Unicode, CUnicode, Int, CBool, Any | |
|
42 | Dict, List, Unicode, CUnicode, Int, CBool, Any, CaselessStrEnum | |
|
42 | 43 | ) |
|
43 | 44 | from IPython.zmq.ipkernel import ( |
|
44 | 45 | flags as ipkernel_flags, |
@@ -151,12 +152,27 b' class IPythonConsoleApp(Configurable):' | |||
|
151 | 152 | # create requested profiles by default, if they don't exist: |
|
152 | 153 | auto_create = CBool(True) |
|
153 | 154 | # connection info: |
|
154 | ip = Unicode(LOCALHOST, config=True, | |
|
155 | ||
|
156 | transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True) | |
|
157 | ||
|
158 | ip = Unicode(config=True, | |
|
155 | 159 | help="""Set the kernel\'s IP address [default localhost]. |
|
156 | 160 | If the IP address is something other than localhost, then |
|
157 | 161 | Consoles on other machines will be able to connect |
|
158 | 162 | to the Kernel, so be careful!""" |
|
159 | 163 | ) |
|
164 | def _ip_default(self): | |
|
165 | if self.transport == 'tcp': | |
|
166 | return LOCALHOST | |
|
167 | else: | |
|
168 | # this can fire early if ip is given, | |
|
169 | # in which case our return value is meaningless | |
|
170 | if not hasattr(self, 'profile_dir'): | |
|
171 | return '' | |
|
172 | ipcdir = os.path.join(self.profile_dir.security_dir, 'kernel-%s' % os.getpid()) | |
|
173 | os.makedirs(ipcdir) | |
|
174 | atexit.register(lambda : shutil.rmtree(ipcdir)) | |
|
175 | return os.path.join(ipcdir, 'ipc') | |
|
160 | 176 | |
|
161 | 177 | sshserver = Unicode('', config=True, |
|
162 | 178 | help="""The SSH server to use to connect to the kernel.""") |
@@ -256,10 +272,10 b' class IPythonConsoleApp(Configurable):' | |||
|
256 | 272 | return |
|
257 | 273 | self.log.debug(u"Loading connection file %s", fname) |
|
258 | 274 | with open(fname) as f: |
|
259 |
|
|
|
260 | cfg = json.loads(s) | |
|
261 | if self.ip == LOCALHOST and 'ip' in cfg: | |
|
262 | # not overridden by config or cl_args | |
|
275 | cfg = json.load(f) | |
|
276 | ||
|
277 | self.transport = cfg.get('transport', 'tcp') | |
|
278 | if 'ip' in cfg: | |
|
263 | 279 | self.ip = cfg['ip'] |
|
264 | 280 | for channel in ('hb', 'shell', 'iopub', 'stdin'): |
|
265 | 281 | name = channel + '_port' |
@@ -268,12 +284,17 b' class IPythonConsoleApp(Configurable):' | |||
|
268 | 284 | setattr(self, name, cfg[name]) |
|
269 | 285 | if 'key' in cfg: |
|
270 | 286 | self.config.Session.key = str_to_bytes(cfg['key']) |
|
287 | ||
|
271 | 288 | |
|
272 | 289 | def init_ssh(self): |
|
273 | 290 | """set up ssh tunnels, if needed.""" |
|
274 | 291 | if not self.sshserver and not self.sshkey: |
|
275 | 292 | return |
|
276 | 293 | |
|
294 | if self.transport != 'tcp': | |
|
295 | self.log.error("Can only use ssh tunnels with TCP sockets, not %s", self.transport) | |
|
296 | return | |
|
297 | ||
|
277 | 298 | if self.sshkey and not self.sshserver: |
|
278 | 299 | # specifying just the key implies that we are connecting directly |
|
279 | 300 | self.sshserver = self.ip |
@@ -326,6 +347,7 b' class IPythonConsoleApp(Configurable):' | |||
|
326 | 347 | |
|
327 | 348 | # Create a KernelManager and start a kernel. |
|
328 | 349 | self.kernel_manager = self.kernel_manager_class( |
|
350 | transport=self.transport, | |
|
329 | 351 | ip=self.ip, |
|
330 | 352 | shell_port=self.shell_port, |
|
331 | 353 | iopub_port=self.iopub_port, |
@@ -149,13 +149,13 b' class AuthenticatedHandler(RequestHandler):' | |||
|
149 | 149 | """A RequestHandler with an authenticated user.""" |
|
150 | 150 | |
|
151 | 151 | def get_current_user(self): |
|
152 |
user_id = self.get_secure_cookie( |
|
|
152 | user_id = self.get_secure_cookie(self.settings['cookie_name']) | |
|
153 | 153 | # For now the user_id should not return empty, but it could eventually |
|
154 | 154 | if user_id == '': |
|
155 | 155 | user_id = 'anonymous' |
|
156 | 156 | if user_id is None: |
|
157 | 157 | # prevent extra Invalid cookie sig warnings: |
|
158 |
self.clear_cookie(' |
|
|
158 | self.clear_cookie(self.settings['cookie_name']) | |
|
159 | 159 | if not self.application.password and not self.application.read_only: |
|
160 | 160 | user_id = 'anonymous' |
|
161 | 161 | return user_id |
@@ -247,7 +247,7 b' class LoginHandler(AuthenticatedHandler):' | |||
|
247 | 247 | pwd = self.get_argument('password', default=u'') |
|
248 | 248 | if self.application.password: |
|
249 | 249 | if passwd_check(self.application.password, pwd): |
|
250 |
self.set_secure_cookie(' |
|
|
250 | self.set_secure_cookie(self.settings['cookie_name'], str(uuid.uuid4())) | |
|
251 | 251 | else: |
|
252 | 252 | self._render(message={'error': 'Invalid password'}) |
|
253 | 253 | return |
@@ -258,7 +258,7 b' class LoginHandler(AuthenticatedHandler):' | |||
|
258 | 258 | class LogoutHandler(AuthenticatedHandler): |
|
259 | 259 | |
|
260 | 260 | def get(self): |
|
261 |
self.clear_cookie(' |
|
|
261 | self.clear_cookie(self.settings['cookie_name']) | |
|
262 | 262 | if self.login_available: |
|
263 | 263 | message = {'info': 'Successfully logged out.'} |
|
264 | 264 | else: |
@@ -435,7 +435,7 b' class AuthenticatedZMQStreamHandler(ZMQStreamHandler):' | |||
|
435 | 435 | self.on_message = self.on_first_message |
|
436 | 436 | |
|
437 | 437 | def get_current_user(self): |
|
438 |
user_id = self.get_secure_cookie( |
|
|
438 | user_id = self.get_secure_cookie(self.settings['cookie_name']) | |
|
439 | 439 | if user_id == '' or (user_id is None and not self.application.password): |
|
440 | 440 | user_id = 'anonymous' |
|
441 | 441 | return user_id |
@@ -42,6 +42,11 b' class NotebookManager(LoggingConfigurable):' | |||
|
42 | 42 | """) |
|
43 | 43 | def _notebook_dir_changed(self, name, old, new): |
|
44 | 44 | """do a bit of validation of the notebook dir""" |
|
45 | if not os.path.isabs(new): | |
|
46 | # If we receive a non-absolute path, make it absolute. | |
|
47 | abs_new = os.path.abspath(new) | |
|
48 | self.notebook_dir = abs_new | |
|
49 | return | |
|
45 | 50 | if os.path.exists(new) and not os.path.isdir(new): |
|
46 | 51 | raise TraitError("notebook dir %r is not a directory" % new) |
|
47 | 52 | if not os.path.exists(new): |
@@ -117,7 +122,7 b' class NotebookManager(LoggingConfigurable):' | |||
|
117 | 122 | # should match the Python in-memory format. |
|
118 | 123 | kwargs['split_lines'] = False |
|
119 | 124 | data = current.writes(nb, format, **kwargs) |
|
120 | name = nb.get('name','notebook') | |
|
125 | name = nb.metadata.get('name','notebook') | |
|
121 | 126 | return last_modified, name, data |
|
122 | 127 | |
|
123 | 128 | def read_notebook_object(self, notebook_id): |
@@ -28,6 +28,7 b' import socket' | |||
|
28 | 28 | import sys |
|
29 | 29 | import threading |
|
30 | 30 | import time |
|
31 | import uuid | |
|
31 | 32 | import webbrowser |
|
32 | 33 | |
|
33 | 34 | # Third party |
@@ -164,6 +165,7 b' class NotebookWebApplication(web.Application):' | |||
|
164 | 165 | static_handler_class = FileFindHandler, |
|
165 | 166 | cookie_secret=os.urandom(1024), |
|
166 | 167 | login_url="%s/login"%(base_project_url.rstrip('/')), |
|
168 | cookie_name='username-%s' % uuid.uuid4(), | |
|
167 | 169 | ) |
|
168 | 170 | |
|
169 | 171 | # allow custom overrides for the tornado web app. |
@@ -468,11 +470,14 b' class NotebookApp(BaseIPythonApplication):' | |||
|
468 | 470 | ssl_options = None |
|
469 | 471 | self.web_app.password = self.password |
|
470 | 472 | self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options) |
|
471 | if ssl_options is None and not self.ip and not (self.read_only and not self.password): | |
|
472 |
|
|
|
473 | 'but not using any encryption or authentication. This is highly ' | |
|
474 | 'insecure and not recommended.') | |
|
475 | ||
|
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.") | |
|
476 | 481 | success = None |
|
477 | 482 | for port in random_ports(self.port, self.port_retries+1): |
|
478 | 483 | try: |
@@ -70,7 +70,7 b' span#notebook_name {' | |||
|
70 | 70 | z-index: 10; |
|
71 | 71 | } |
|
72 | 72 | |
|
73 |
|
|
|
73 | .toolbar { | |
|
74 | 74 | padding: 3px 15px; |
|
75 | 75 | } |
|
76 | 76 | |
@@ -122,6 +122,10 b' div#pager_splitter {' | |||
|
122 | 122 | height: 8px; |
|
123 | 123 | } |
|
124 | 124 | |
|
125 | #pager_container { | |
|
126 | position : relative; | |
|
127 | } | |
|
128 | ||
|
125 | 129 | div#pager { |
|
126 | 130 | padding: 15px; |
|
127 | 131 | overflow: auto; |
@@ -234,7 +238,7 b' div.output_area {' | |||
|
234 | 238 | /* This class is for the output subarea inside the output_area and after |
|
235 | 239 | the prompt div. */ |
|
236 | 240 | div.output_subarea { |
|
237 |
padding: 0.4em 0.4em 0.4em |
|
|
241 | padding: 0.44em 0.4em 0.4em 1px; | |
|
238 | 242 | } |
|
239 | 243 | |
|
240 | 244 | /* The rest of the output_* classes are for special styling of the different |
@@ -33,7 +33,7 b' span#ipython_notebook {' | |||
|
33 | 33 | padding: 2px 2px 2px 5px; |
|
34 | 34 | } |
|
35 | 35 | |
|
36 |
span#ipython_notebook |
|
|
36 | span#ipython_notebook img { | |
|
37 | 37 | font-family: Verdana, "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif; |
|
38 | 38 | height: 24px; |
|
39 | 39 | text-decoration:none; |
@@ -44,7 +44,7 b'' | |||
|
44 | 44 | opacity: 1; |
|
45 | 45 | } |
|
46 | 46 | } |
|
47 | .tooltip a { | |
|
47 | .ipython_tooltip a { | |
|
48 | 48 | float: right; |
|
49 | 49 | } |
|
50 | 50 | /*properties of tooltip after "expand"*/ |
@@ -81,7 +81,7 b'' | |||
|
81 | 81 | |
|
82 | 82 | padding-right: 30px; |
|
83 | 83 | } |
|
84 | .tooltip { | |
|
84 | .ipython_tooltip { | |
|
85 | 85 | max-width: 700px; |
|
86 | 86 | border-radius: 4px; |
|
87 | 87 | -moz-box-shadow: 0px 6px 10px -1px #adadad; |
@@ -20,6 +20,8 b' var IPython = (function (IPython) {' | |||
|
20 | 20 | this.selected = false; |
|
21 | 21 | this.element = null; |
|
22 | 22 | this.metadata = {}; |
|
23 | // load this from metadata later ? | |
|
24 | this.user_highlight == 'auto'; | |
|
23 | 25 | this.create_element(); |
|
24 | 26 | if (this.element !== null) { |
|
25 | 27 | this.element.data("cell", this); |
@@ -48,15 +50,13 b' var IPython = (function (IPython) {' | |||
|
48 | 50 | }); |
|
49 | 51 | }; |
|
50 | 52 | |
|
51 | ||
|
52 | // typeset with MathJax if MathJax is available | |
|
53 | 53 | Cell.prototype.typeset = function () { |
|
54 | 54 | if (window.MathJax){ |
|
55 | MathJax.Hub.Queue(["Typeset",MathJax.Hub]); | |
|
55 | var cell_math = this.element.get(0); | |
|
56 | MathJax.Hub.Queue(["Typeset",MathJax.Hub,cell_math]); | |
|
56 | 57 | } |
|
57 | 58 | }; |
|
58 | 59 | |
|
59 | ||
|
60 | 60 | Cell.prototype.select = function () { |
|
61 | 61 | this.element.addClass('ui-widget-content ui-corner-all'); |
|
62 | 62 | this.selected = true; |
@@ -154,6 +154,61 b' var IPython = (function (IPython) {' | |||
|
154 | 154 | this.code_mirror.refresh(); |
|
155 | 155 | }; |
|
156 | 156 | |
|
157 | Cell.prototype.force_highlight = function(mode) { | |
|
158 | this.user_highlight = mode; | |
|
159 | this.auto_highlight(); | |
|
160 | }; | |
|
161 | ||
|
162 | Cell.prototype._auto_highlight = function (modes) { | |
|
163 | //Here we handle manually selected modes | |
|
164 | if( this.user_highlight != undefined && this.user_highlight != 'auto' ) | |
|
165 | { | |
|
166 | var mode = this.user_highlight; | |
|
167 | CodeMirror.autoLoadMode(this.code_mirror, mode); | |
|
168 | this.code_mirror.setOption('mode', mode); | |
|
169 | return; | |
|
170 | } | |
|
171 | var first_line = this.code_mirror.getLine(0); | |
|
172 | // loop on every pairs | |
|
173 | for( var mode in modes) { | |
|
174 | var regs = modes[mode]['reg']; | |
|
175 | // only one key every time but regexp can't be keys... | |
|
176 | for(var reg in regs ) { | |
|
177 | // here we handle non magic_modes | |
|
178 | if(first_line.match(regs[reg]) != null) { | |
|
179 | if (mode.search('magic_') != 0) { | |
|
180 | this.code_mirror.setOption('mode',mode); | |
|
181 | CodeMirror.autoLoadMode(this.code_mirror, mode); | |
|
182 | return; | |
|
183 | } | |
|
184 | var open = modes[mode]['open']|| "%%"; | |
|
185 | var close = modes[mode]['close']|| "%%end"; | |
|
186 | var mmode = mode; | |
|
187 | mode = mmode.substr(6); | |
|
188 | CodeMirror.autoLoadMode(this.code_mirror, mode); | |
|
189 | // create on the fly a mode that swhitch between | |
|
190 | // plain/text and smth else otherwise `%%` is | |
|
191 | // source of some highlight issues. | |
|
192 | // we use patchedGetMode to circumvent a bug in CM | |
|
193 | CodeMirror.defineMode(mmode , function(config) { | |
|
194 | return CodeMirror.multiplexingMode( | |
|
195 | CodeMirror.patchedGetMode(config, 'text/plain'), | |
|
196 | // always set someting on close | |
|
197 | {open: open, close: close, | |
|
198 | mode: CodeMirror.patchedGetMode(config, mode), | |
|
199 | delimStyle: "delimit" | |
|
200 | } | |
|
201 | ); | |
|
202 | }); | |
|
203 | this.code_mirror.setOption('mode', mmode); | |
|
204 | return; | |
|
205 | } | |
|
206 | } | |
|
207 | } | |
|
208 | // fallback on default (python) | |
|
209 | var default_mode = this.default_mode || 'text/plain'; | |
|
210 | this.code_mirror.setOption('mode', default_mode); | |
|
211 | }; | |
|
157 | 212 | |
|
158 | 213 | IPython.Cell = Cell; |
|
159 | 214 |
@@ -14,6 +14,7 b' var IPython = (function (IPython) {' | |||
|
14 | 14 | |
|
15 | 15 | var utils = IPython.utils; |
|
16 | 16 | var key = IPython.utils.keycodes; |
|
17 | CodeMirror.modeURL = "/static/codemirror/mode/%N/%N.js"; | |
|
17 | 18 | |
|
18 | 19 | var CodeCell = function (kernel) { |
|
19 | 20 | // The kernel doesn't have to be set at creation time, in that case |
@@ -23,13 +24,23 b' var IPython = (function (IPython) {' | |||
|
23 | 24 | this.input_prompt_number = null; |
|
24 | 25 | this.tooltip_on_tab = true; |
|
25 | 26 | this.collapsed = false; |
|
27 | this.default_mode = 'python'; | |
|
26 | 28 | IPython.Cell.apply(this, arguments); |
|
29 | ||
|
30 | var that = this; | |
|
31 | this.element.focusout( | |
|
32 | function() { that.auto_highlight(); } | |
|
33 | ); | |
|
27 | 34 | }; |
|
28 | 35 | |
|
29 | 36 | |
|
30 | 37 | CodeCell.prototype = new IPython.Cell(); |
|
31 | 38 | |
|
32 | 39 | |
|
40 | CodeCell.prototype.auto_highlight = function () { | |
|
41 | this._auto_highlight(IPython.config.cell_magic_highlight) | |
|
42 | }; | |
|
43 | ||
|
33 | 44 | CodeCell.prototype.create_element = function () { |
|
34 | 45 | var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox'); |
|
35 | 46 | cell.attr('tabindex','2'); |
@@ -76,6 +87,9 b' var IPython = (function (IPython) {' | |||
|
76 | 87 | }; |
|
77 | 88 | |
|
78 | 89 | var cur = editor.getCursor(); |
|
90 | if (event.keyCode === key.ENTER){ | |
|
91 | this.auto_highlight(); | |
|
92 | } | |
|
79 | 93 | |
|
80 | 94 | if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) { |
|
81 | 95 | // Always ignore shift-enter in CodeMirror as we handle it. |
@@ -109,6 +123,7 b' var IPython = (function (IPython) {' | |||
|
109 | 123 | } else if (event.keyCode === key.TAB && event.type == 'keydown') { |
|
110 | 124 | // Tab completion. |
|
111 | 125 | //Do not trim here because of tooltip |
|
126 | if (editor.somethingSelected()){return false} | |
|
112 | 127 | var pre_cursor = editor.getRange({line:cur.line,ch:0},cur); |
|
113 | 128 | if (pre_cursor.trim() === "") { |
|
114 | 129 | // Don't autocomplete if the part of the line before the cursor |
@@ -172,6 +187,7 b' var IPython = (function (IPython) {' | |||
|
172 | 187 | IPython.Cell.prototype.select.apply(this); |
|
173 | 188 | this.code_mirror.refresh(); |
|
174 | 189 | this.code_mirror.focus(); |
|
190 | this.auto_highlight(); | |
|
175 | 191 | // We used to need an additional refresh() after the focus, but |
|
176 | 192 | // it appears that this has been fixed in CM. This bug would show |
|
177 | 193 | // up on FF when a newly loaded markdown cell was edited. |
@@ -210,10 +226,31 b' var IPython = (function (IPython) {' | |||
|
210 | 226 | }; |
|
211 | 227 | |
|
212 | 228 | |
|
229 | ||
|
230 | ||
|
231 | ||
|
232 | CodeCell.input_prompt_classical = function (prompt_value, lines_number) { | |
|
233 | var ns = prompt_value || " "; | |
|
234 | return 'In [' + ns + ']:' | |
|
235 | }; | |
|
236 | ||
|
237 | CodeCell.input_prompt_continuation = function (prompt_value, lines_number) { | |
|
238 | var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)]; | |
|
239 | for(var i=1; i < lines_number; i++){html.push(['...:'])}; | |
|
240 | return html.join('</br>') | |
|
241 | }; | |
|
242 | ||
|
243 | CodeCell.input_prompt_function = CodeCell.input_prompt_classical; | |
|
244 | ||
|
245 | ||
|
213 | 246 | CodeCell.prototype.set_input_prompt = function (number) { |
|
247 | var nline = 1 | |
|
248 | if( this.code_mirror != undefined) { | |
|
249 | nline = this.code_mirror.lineCount(); | |
|
250 | } | |
|
214 | 251 | this.input_prompt_number = number; |
|
215 | var ns = number || " "; | |
|
216 |
this.element.find('div.input_prompt').html( |
|
|
252 | var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline); | |
|
253 | this.element.find('div.input_prompt').html(prompt_html); | |
|
217 | 254 | }; |
|
218 | 255 | |
|
219 | 256 | |
@@ -267,6 +304,7 b' var IPython = (function (IPython) {' | |||
|
267 | 304 | // make this value the starting point, so that we can only undo |
|
268 | 305 | // to this state, instead of a blank cell |
|
269 | 306 | this.code_mirror.clearHistory(); |
|
307 | this.auto_highlight(); | |
|
270 | 308 | } |
|
271 | 309 | if (data.prompt_number !== undefined) { |
|
272 | 310 | this.set_input_prompt(data.prompt_number); |
@@ -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']; |
@@ -32,10 +32,10 b' var IPython = (function (IPython) {' | |||
|
32 | 32 | } |
|
33 | 33 | var menubar_height = $('div#menubar').outerHeight(true); |
|
34 | 34 | var toolbar_height; |
|
35 | if ($('div#toolbar').css('display') === 'none') { | |
|
35 | if ($('div#maintoolbar').css('display') === 'none') { | |
|
36 | 36 | toolbar_height = 0; |
|
37 | 37 | } else { |
|
38 | toolbar_height = $('div#toolbar').outerHeight(true); | |
|
38 | toolbar_height = $('div#maintoolbar').outerHeight(true); | |
|
39 | 39 | } |
|
40 | 40 | return h-header_height-menubar_height-toolbar_height; // content height |
|
41 | 41 | } |
@@ -1,29 +1,33 b'' | |||
|
1 | 1 | //---------------------------------------------------------------------------- |
|
2 |
// Copyright (C) 2008-201 |
|
|
2 | // Copyright (C) 2008-2012 The IPython Development Team | |
|
3 | 3 | // |
|
4 | 4 | // Distributed under the terms of the BSD License. The full license is in |
|
5 | 5 | // the file COPYING, distributed as part of this software. |
|
6 | 6 | //---------------------------------------------------------------------------- |
|
7 | 7 | |
|
8 | 8 | //============================================================================ |
|
9 |
// MathJax |
|
|
9 | // MathJax utility functions | |
|
10 | 10 | //============================================================================ |
|
11 | 11 | |
|
12 | var IPython = (function (IPython) { | |
|
12 | IPython.namespace('IPython.mathjaxutils'); | |
|
13 | 13 | |
|
14 |
|
|
|
15 | if (window.MathJax) { | |
|
14 | IPython.mathjaxutils = (function (IPython) { | |
|
15 | ||
|
16 | var init = function () { | |
|
17 | if (window.MathJax) { | |
|
16 | 18 | // MathJax loaded |
|
17 | 19 | MathJax.Hub.Config({ |
|
18 | 20 | tex2jax: { |
|
19 | 21 | inlineMath: [ ['$','$'], ["\\(","\\)"] ], |
|
20 | displayMath: [ ['$$','$$'], ["\\[","\\]"] ] | |
|
22 | displayMath: [ ['$$','$$'], ["\\[","\\]"] ], | |
|
23 | processEnvironments: true | |
|
21 | 24 | }, |
|
22 | 25 | displayAlign: 'left', // Change this to 'center' to center equations. |
|
23 | 26 | "HTML-CSS": { |
|
24 | 27 | styles: {'.MathJax_Display': {"margin": 0}} |
|
25 | 28 | } |
|
26 | 29 | }); |
|
30 | MathJax.Hub.Configured(); | |
|
27 | 31 | } else if (window.mathjax_url != "") { |
|
28 | 32 | // Don't have MathJax, but should. Show dialog. |
|
29 | 33 | var dialog = $('<div></div>') |
@@ -74,10 +78,164 b' var IPython = (function (IPython) {' | |||
|
74 | 78 | }; |
|
75 | 79 | }; |
|
76 | 80 | |
|
81 | // Some magic for deferring mathematical expressions to MathJax | |
|
82 | // by hiding them from the Markdown parser. | |
|
83 | // Some of the code here is adapted with permission from Davide Cervone | |
|
84 | // under the terms of the Apache2 license governing the MathJax project. | |
|
85 | // Other minor modifications are also due to StackExchange and are used with | |
|
86 | // permission. | |
|
87 | ||
|
88 | var inline = "$"; // the inline math delimiter | |
|
89 | var blocks, start, end, last, braces; // used in searching for math | |
|
90 | var math; // stores math until pagedown (Markdown parser) is done | |
|
91 | ||
|
92 | // MATHSPLIT contains the pattern for math delimiters and special symbols | |
|
93 | // needed for searching for math in the text input. | |
|
94 | var MATHSPLIT = /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[\\{}$]|[{}]|(?:\n\s*)+|@@\d+@@)/i; | |
|
95 | ||
|
96 | // The math is in blocks i through j, so | |
|
97 | // collect it into one block and clear the others. | |
|
98 | // Replace &, <, and > by named entities. | |
|
99 | // For IE, put <br> at the ends of comments since IE removes \n. | |
|
100 | // Clear the current math positions and store the index of the | |
|
101 | // math, then push the math string onto the storage array. | |
|
102 | // The preProcess function is called on all blocks if it has been passed in | |
|
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, "&") // use HTML entity for & | |
|
106 | .replace(/</g, "<") // use HTML entity for < | |
|
107 | .replace(/>/g, ">") // use HTML entity for > | |
|
108 | ; | |
|
109 | if (hub.Browser.isMSIE) { | |
|
110 | block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n") | |
|
111 | } | |
|
112 | while (j > i) { | |
|
113 | blocks[j] = ""; | |
|
114 | j--; | |
|
115 | } | |
|
116 | blocks[i] = "@@" + math.length + "@@"; // replace the current block text with a unique tag to find later | |
|
117 | if (pre_process) | |
|
118 | block = pre_process(block); | |
|
119 | math.push(block); | |
|
120 | start = end = last = null; | |
|
121 | } | |
|
122 | ||
|
123 | // Break up the text into its component parts and search | |
|
124 | // through them for math delimiters, braces, linebreaks, etc. | |
|
125 | // Math delimiters must match and braces must balance. | |
|
126 | // Don't allow math to pass through a double linebreak | |
|
127 | // (which will be a paragraph). | |
|
128 | // | |
|
129 | var remove_math = function (text) { | |
|
130 | if (!window.MathJax) { | |
|
131 | return text; | |
|
132 | } | |
|
133 | ||
|
134 | start = end = last = null; // for tracking math delimiters | |
|
135 | math = []; // stores math strings for later | |
|
136 | ||
|
137 | // Except for extreme edge cases, this should catch precisely those pieces of the markdown | |
|
138 | // source that will later be turned into code spans. While MathJax will not TeXify code spans, | |
|
139 | // we still have to consider them at this point; the following issue has happened several times: | |
|
140 | // | |
|
141 | // `$foo` and `$bar` are varibales. --> <code>$foo ` and `$bar</code> are variables. | |
|
142 | ||
|
143 | var hasCodeSpans = /`/.test(text), | |
|
144 | de_tilde; | |
|
145 | if (hasCodeSpans) { | |
|
146 | text = text.replace(/~/g, "~T").replace(/(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm, function (wholematch) { | |
|
147 | return wholematch.replace(/\$/g, "~D"); | |
|
148 | }); | |
|
149 | de_tilde = function (text) { return text.replace(/~([TD])/g, function (wholematch, character) { return { T: "~", D: "$" }[character]; }) }; | |
|
150 | } else { | |
|
151 | de_tilde = function (text) { return text; }; | |
|
152 | } | |
|
153 | ||
|
154 | blocks = IPython.utils.regex_split(text.replace(/\r\n?/g, "\n"),MATHSPLIT); | |
|
155 | ||
|
156 | for (var i = 1, m = blocks.length; i < m; i += 2) { | |
|
157 | var block = blocks[i]; | |
|
158 | if (block.charAt(0) === "@") { | |
|
159 | // | |
|
160 | // Things that look like our math markers will get | |
|
161 | // stored and then retrieved along with the math. | |
|
162 | // | |
|
163 | blocks[i] = "@@" + math.length + "@@"; | |
|
164 | math.push(block); | |
|
165 | } | |
|
166 | else if (start) { | |
|
167 | // | |
|
168 | // If we are in math, look for the end delimiter, | |
|
169 | // but don't go past double line breaks, and | |
|
170 | // and balance braces within the math. | |
|
171 | // | |
|
172 | if (block === end) { | |
|
173 | if (braces) { | |
|
174 | last = i | |
|
175 | } | |
|
176 | else { | |
|
177 | process_math(start, i, de_tilde) | |
|
178 | } | |
|
179 | } | |
|
180 | else if (block.match(/\n.*\n/)) { | |
|
181 | if (last) { | |
|
182 | i = last; | |
|
183 | process_math(start, i, de_tilde) | |
|
184 | } | |
|
185 | start = end = last = null; | |
|
186 | braces = 0; | |
|
187 | } | |
|
188 | else if (block === "{") { | |
|
189 | braces++ | |
|
190 | } | |
|
191 | else if (block === "}" && braces) { | |
|
192 | braces-- | |
|
193 | } | |
|
194 | } | |
|
195 | else { | |
|
196 | // | |
|
197 | // Look for math start delimiters and when | |
|
198 | // found, set up the end delimiter. | |
|
199 | // | |
|
200 | if (block === inline || block === "$$") { | |
|
201 | start = i; | |
|
202 | end = block; | |
|
203 | braces = 0; | |
|
204 | } | |
|
205 | else if (block.substr(1, 5) === "begin") { | |
|
206 | start = i; | |
|
207 | end = "\\end" + block.substr(6); | |
|
208 | braces = 0; | |
|
209 | } | |
|
210 | } | |
|
211 | } | |
|
212 | if (last) { | |
|
213 | process_math(start, last, de_tilde) | |
|
214 | } | |
|
215 | return de_tilde(blocks.join("")); | |
|
216 | } | |
|
77 | 217 | |
|
78 | // Set module variables | |
|
79 | IPython.init_mathjax = init_mathjax; | |
|
218 | // | |
|
219 | // Put back the math strings that were saved, | |
|
220 | // and clear the math array (no need to keep it around). | |
|
221 | // | |
|
222 | var replace_math = function (text) { | |
|
223 | if (!window.MathJax) { | |
|
224 | return text; | |
|
225 | } | |
|
80 | 226 | |
|
81 | return IPython; | |
|
227 | text = text.replace(/@@(\d+)@@/g, function (match, n) { | |
|
228 | return math[n] | |
|
229 | }); | |
|
230 | math = null; | |
|
231 | return text; | |
|
232 | } | |
|
233 | ||
|
234 | return { | |
|
235 | init : init, | |
|
236 | process_math : process_math, | |
|
237 | remove_math : remove_math, | |
|
238 | replace_math : replace_math | |
|
239 | }; | |
|
82 | 240 | |
|
83 | 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; |
@@ -304,6 +312,16 b' var IPython = (function (IPython) {' | |||
|
304 | 312 | }); |
|
305 | 313 | }; |
|
306 | 314 | |
|
315 | Notebook.prototype.scroll_to_cell = function (cell_number, time) { | |
|
316 | var cells = this.get_cells(); | |
|
317 | var time = time || 0; | |
|
318 | cell_number = Math.min(cells.length-1,cell_number); | |
|
319 | cell_number = Math.max(0 ,cell_number); | |
|
320 | scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ; | |
|
321 | this.element.animate({scrollTop:scroll_value}, time); | |
|
322 | return scroll_value; | |
|
323 | }; | |
|
324 | ||
|
307 | 325 | |
|
308 | 326 | Notebook.prototype.scroll_to_bottom = function () { |
|
309 | 327 | this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0); |
@@ -526,13 +544,19 b' var IPython = (function (IPython) {' | |||
|
526 | 544 | |
|
527 | 545 | Notebook.prototype.delete_cell = function (index) { |
|
528 | 546 | var i = this.index_or_selected(index); |
|
547 | var cell = this.get_selected_cell(); | |
|
548 | this.undelete_backup = cell.toJSON(); | |
|
529 | 549 | if (this.is_valid_cell_index(i)) { |
|
530 | 550 | var ce = this.get_cell_element(i); |
|
531 | 551 | ce.remove(); |
|
532 | 552 | if (i === (this.ncells())) { |
|
533 | 553 | this.select(i-1); |
|
554 | this.undelete_index = i - 1; | |
|
555 | this.undelete_below = true; | |
|
534 | 556 | } else { |
|
535 | 557 | this.select(i); |
|
558 | this.undelete_index = i; | |
|
559 | this.undelete_below = false; | |
|
536 | 560 | }; |
|
537 | 561 | this.dirty = true; |
|
538 | 562 | }; |
@@ -540,11 +564,21 b' var IPython = (function (IPython) {' | |||
|
540 | 564 | }; |
|
541 | 565 | |
|
542 | 566 | |
|
567 | Notebook.prototype.insert_cell_at_bottom = function (type){ | |
|
568 | var len = this.ncells(); | |
|
569 | return this.insert_cell_below(type,len-1); | |
|
570 | } | |
|
571 | ||
|
543 | 572 | Notebook.prototype.insert_cell_below = function (type, index) { |
|
544 | 573 | // type = ('code','html','markdown') |
|
545 | 574 | // index = cell index or undefined to insert below selected |
|
546 | 575 | index = this.index_or_selected(index); |
|
547 | 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 | } | |
|
548 | 582 | if (this.ncells() === 0 || this.is_valid_cell_index(index)) { |
|
549 | 583 | if (type === 'code') { |
|
550 | 584 | cell = new IPython.CodeCell(this.kernel); |
@@ -579,6 +613,9 b' var IPython = (function (IPython) {' | |||
|
579 | 613 | // index = cell index or undefined to insert above selected |
|
580 | 614 | index = this.index_or_selected(index); |
|
581 | 615 | var cell = null; |
|
616 | if (this.undelete_index !== null && index <= this.undelete_index) { | |
|
617 | this.undelete_index = this.undelete_index + 1; | |
|
618 | } | |
|
582 | 619 | if (this.ncells() === 0 || this.is_valid_cell_index(index)) { |
|
583 | 620 | if (type === 'code') { |
|
584 | 621 | cell = new IPython.CodeCell(this.kernel); |
@@ -741,8 +778,8 b' var IPython = (function (IPython) {' | |||
|
741 | 778 | Notebook.prototype.enable_paste = function () { |
|
742 | 779 | var that = this; |
|
743 | 780 | if (!this.paste_enabled) { |
|
744 | $('#paste_cell').removeClass('ui-state-disabled') | |
|
745 | .on('click', function () {that.paste_cell();}); | |
|
781 | $('#paste_cell_replace').removeClass('ui-state-disabled') | |
|
782 | .on('click', function () {that.paste_cell_replace();}); | |
|
746 | 783 | $('#paste_cell_above').removeClass('ui-state-disabled') |
|
747 | 784 | .on('click', function () {that.paste_cell_above();}); |
|
748 | 785 | $('#paste_cell_below').removeClass('ui-state-disabled') |
@@ -754,7 +791,7 b' var IPython = (function (IPython) {' | |||
|
754 | 791 | |
|
755 | 792 | Notebook.prototype.disable_paste = function () { |
|
756 | 793 | if (this.paste_enabled) { |
|
757 | $('#paste_cell').addClass('ui-state-disabled').off('click'); | |
|
794 | $('#paste_cell_replace').addClass('ui-state-disabled').off('click'); | |
|
758 | 795 | $('#paste_cell_above').addClass('ui-state-disabled').off('click'); |
|
759 | 796 | $('#paste_cell_below').addClass('ui-state-disabled').off('click'); |
|
760 | 797 | this.paste_enabled = false; |
@@ -774,7 +811,7 b' var IPython = (function (IPython) {' | |||
|
774 | 811 | }; |
|
775 | 812 | |
|
776 | 813 | |
|
777 | Notebook.prototype.paste_cell = function () { | |
|
814 | Notebook.prototype.paste_cell_replace = function () { | |
|
778 | 815 | if (this.clipboard !== null && this.paste_enabled) { |
|
779 | 816 | var cell_data = this.clipboard; |
|
780 | 817 | var new_cell = this.insert_cell_above(cell_data.cell_type); |
@@ -803,6 +840,33 b' var IPython = (function (IPython) {' | |||
|
803 | 840 | }; |
|
804 | 841 | }; |
|
805 | 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 | } | |
|
806 | 870 | |
|
807 | 871 | // Split/merge |
|
808 | 872 | |
@@ -1034,13 +1098,25 b' var IPython = (function (IPython) {' | |||
|
1034 | 1098 | }; |
|
1035 | 1099 | |
|
1036 | 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 | ||
|
1037 | 1110 | Notebook.prototype.execute_all_cells = function () { |
|
1038 |
|
|
|
1039 | 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++) { | |
|
1040 | 1117 | this.select(i); |
|
1041 | 1118 | this.execute_selected_cell({add_new:false}); |
|
1042 | 1119 | }; |
|
1043 | this.scroll_to_bottom(); | |
|
1044 | 1120 | }; |
|
1045 | 1121 | |
|
1046 | 1122 | // Persistance and loading |
@@ -1,5 +1,5 b'' | |||
|
1 | 1 | //---------------------------------------------------------------------------- |
|
2 |
// Copyright (C) 20 |
|
|
2 | // Copyright (C) 2011 The IPython Development Team | |
|
3 | 3 | // |
|
4 | 4 | // Distributed under the terms of the BSD License. The full license is in |
|
5 | 5 | // the file COPYING, distributed as part of this software. |
@@ -12,7 +12,26 b'' | |||
|
12 | 12 | |
|
13 | 13 | $(document).ready(function () { |
|
14 | 14 | |
|
15 | IPython.init_mathjax(); | |
|
15 | // monkey patch CM to be able to syntax highlight cell magics | |
|
16 | // bug reported upstream, | |
|
17 | // see https://github.com/marijnh/CodeMirror2/issues/670 | |
|
18 | if(CodeMirror.getMode(1,'text/plain').indent == undefined ){ | |
|
19 | console.log('patching CM for undefined indent'); | |
|
20 | CodeMirror.modes.null = function() { return {token: function(stream) {stream.skipToEnd();},indent : function(){return 0}}} | |
|
21 | } | |
|
22 | ||
|
23 | CodeMirror.patchedGetMode = function(config, mode){ | |
|
24 | var cmmode = CodeMirror.getMode(config, mode); | |
|
25 | if(cmmode.indent == null) | |
|
26 | { | |
|
27 | console.log('patch mode "' , mode, '" on the fly'); | |
|
28 | cmmode.indent = function(){return 0}; | |
|
29 | } | |
|
30 | return cmmode; | |
|
31 | } | |
|
32 | // end monkey patching CodeMirror | |
|
33 | ||
|
34 | IPython.mathjaxutils.init(); | |
|
16 | 35 | |
|
17 | 36 | IPython.read_only = $('body').data('readOnly') === 'True'; |
|
18 | 37 | $('div#main_app').addClass('border-box-sizing ui-widget'); |
@@ -29,7 +48,7 b' $(document).ready(function () {' | |||
|
29 | 48 | IPython.notebook = new IPython.Notebook('div#notebook'); |
|
30 | 49 | IPython.save_widget = new IPython.SaveWidget('span#save_widget'); |
|
31 | 50 | IPython.menubar = new IPython.MenuBar('#menubar') |
|
32 | IPython.toolbar = new IPython.ToolBar('#toolbar') | |
|
51 | IPython.toolbar = new IPython.MainToolBar('#maintoolbar') | |
|
33 | 52 | IPython.tooltip = new IPython.Tooltip() |
|
34 | 53 | IPython.notification_area = new IPython.NotificationArea('#notification_area') |
|
35 | 54 | IPython.notification_area.init_notification_widgets(); |
@@ -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 | } |
@@ -15,6 +15,7 b' var IPython = (function (IPython) {' | |||
|
15 | 15 | |
|
16 | 16 | var Pager = function (pager_selector, pager_splitter_selector) { |
|
17 | 17 | this.pager_element = $(pager_selector); |
|
18 | this.pager_button_area = $('#pager_button_area'); | |
|
18 | 19 | var that = this; |
|
19 | 20 | this.percentage_height = 0.40; |
|
20 | 21 | this.pager_splitter_element = $(pager_splitter_selector) |
@@ -39,9 +40,24 b' var IPython = (function (IPython) {' | |||
|
39 | 40 | }); |
|
40 | 41 | this.expanded = false; |
|
41 | 42 | this.style(); |
|
43 | this.create_button_area(); | |
|
42 | 44 | this.bind_events(); |
|
43 | 45 | }; |
|
44 | 46 | |
|
47 | Pager.prototype.create_button_area = function(){ | |
|
48 | var that = this; | |
|
49 | this.pager_button_area.append( | |
|
50 | $('<a>').attr('role', "button") | |
|
51 | .attr('title',"open the pager in an external window") | |
|
52 | .addClass('ui-button') | |
|
53 | .click(function(){that.detach()}) | |
|
54 | .attr('style','position: absolute; right: 10px;') | |
|
55 | .append( | |
|
56 | $('<span>').addClass("ui-icon ui-icon-extlink") | |
|
57 | ) | |
|
58 | ) | |
|
59 | }; | |
|
60 | ||
|
45 | 61 | Pager.prototype.style = function () { |
|
46 | 62 | this.pager_splitter_element.addClass('border-box-sizing ui-widget ui-state-default'); |
|
47 | 63 | this.pager_element.addClass('border-box-sizing ui-widget'); |
@@ -114,6 +130,26 b' var IPython = (function (IPython) {' | |||
|
114 | 130 | this.pager_element.empty(); |
|
115 | 131 | }; |
|
116 | 132 | |
|
133 | Pager.prototype.detach = function(){ | |
|
134 | var w = window.open("","_blank") | |
|
135 | $(w.document.head) | |
|
136 | .append( | |
|
137 | $('<link>') | |
|
138 | .attr('rel',"stylesheet") | |
|
139 | .attr('href',"/static/css/notebook.css") | |
|
140 | .attr('type',"text/css") | |
|
141 | ) | |
|
142 | .append( | |
|
143 | $('<title>').text("IPython Pager") | |
|
144 | ); | |
|
145 | var pager_body = $(w.document.body) | |
|
146 | pager_body.attr('style','overflow:scroll'); | |
|
147 | ||
|
148 | pager_body.append(this.pager_element.children()) | |
|
149 | w.document.close(); | |
|
150 | this.collapse(); | |
|
151 | ||
|
152 | } | |
|
117 | 153 | |
|
118 | 154 | Pager.prototype.append_text = function (text) { |
|
119 | 155 | var toinsert = $("<div/>").addClass("output_area output_stream"); |
@@ -12,7 +12,7 b'' | |||
|
12 | 12 | |
|
13 | 13 | $(document).ready(function () { |
|
14 | 14 | |
|
15 |
IPython. |
|
|
15 | IPython.mathjaxutils.init(); | |
|
16 | 16 | |
|
17 | 17 | IPython.read_only = $('body').data('readOnly') === 'True'; |
|
18 | 18 | $('div#main_app').addClass('border-box-sizing ui-widget'); |
@@ -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'}, |
@@ -1,5 +1,5 b'' | |||
|
1 | 1 | //---------------------------------------------------------------------------- |
|
2 |
// Copyright (C) 2008-201 |
|
|
2 | // Copyright (C) 2008-2012 The IPython Development Team | |
|
3 | 3 | // |
|
4 | 4 | // Distributed under the terms of the BSD License. The full license is in |
|
5 | 5 | // the file COPYING, distributed as part of this software. |
@@ -221,7 +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 | text = IPython.mathjaxutils.remove_math(text) | |
|
224 | 225 | var html = IPython.markdown_converter.makeHtml(text); |
|
226 | html = IPython.mathjaxutils.replace_math(html) | |
|
225 | 227 | try { |
|
226 | 228 | this.set_rendered(html); |
|
227 | 229 | } catch (e) { |
@@ -231,7 +233,6 b' var IPython = (function (IPython) {' | |||
|
231 | 233 | "Error rendering Markdown!<br/>" + e.toString()) |
|
232 | 234 | ); |
|
233 | 235 | } |
|
234 | this.typeset() | |
|
235 | 236 | this.element.find('div.text_cell_input').hide(); |
|
236 | 237 | this.element.find("div.text_cell_render").show(); |
|
237 | 238 | var code_snippets = this.element.find("pre > code"); |
@@ -246,6 +247,7 b' var IPython = (function (IPython) {' | |||
|
246 | 247 | |
|
247 | 248 | return '<code class="prettyprint">' + code + '</code>'; |
|
248 | 249 | }); |
|
250 | this.typeset() | |
|
249 | 251 | this.rendered = true; |
|
250 | 252 | } |
|
251 | 253 | }; |
@@ -258,11 +260,19 b' var IPython = (function (IPython) {' | |||
|
258 | 260 | this.code_mirror_mode = 'rst'; |
|
259 | 261 | IPython.TextCell.apply(this, arguments); |
|
260 | 262 | this.cell_type = 'raw'; |
|
263 | var that = this | |
|
264 | ||
|
265 | this.element.focusout( | |
|
266 | function() { that.auto_highlight(); } | |
|
267 | ); | |
|
261 | 268 | }; |
|
262 | 269 | |
|
263 | 270 | |
|
264 | 271 | RawCell.prototype = new TextCell(); |
|
265 | 272 | |
|
273 | RawCell.prototype.auto_highlight = function () { | |
|
274 | this._auto_highlight(IPython.config.raw_cell_highlight); | |
|
275 | }; | |
|
266 | 276 | |
|
267 | 277 | RawCell.prototype.render = function () { |
|
268 | 278 | this.rendered = true; |
@@ -1,5 +1,5 b'' | |||
|
1 | 1 | //---------------------------------------------------------------------------- |
|
2 |
// Copyright (C) 2008 |
|
|
2 | // Copyright (C) 2008 The IPython Development Team | |
|
3 | 3 | // |
|
4 | 4 | // Distributed under the terms of the BSD License. The full license is in |
|
5 | 5 | // the file COPYING, distributed as part of this software. |
@@ -16,132 +16,76 b' var IPython = (function (IPython) {' | |||
|
16 | 16 | if (this.selector !== undefined) { |
|
17 | 17 | this.element = $(selector); |
|
18 | 18 | this.style(); |
|
19 | this.bind_events(); | |
|
20 | 19 | } |
|
21 | 20 | }; |
|
22 | 21 | |
|
22 | // add a group of button into the current toolbar. | |
|
23 | // | |
|
24 | // First argument : Mandatory | |
|
25 | // list of dict as argument, each dict should contain | |
|
26 | // 3 mandatory keys and values : | |
|
27 | // label : string -- the text to show on hover | |
|
28 | // icon : string -- the jQuery-ui icon to add on this button | |
|
29 | // callback : function -- the callback to execute on a click | |
|
30 | // | |
|
31 | // and optionally an 'id' key that is assigned to the button element | |
|
32 | // | |
|
33 | // Second Argument, optional, | |
|
34 | // string reprensenting the id to give to the button group. | |
|
35 | // | |
|
36 | // Example | |
|
37 | // | |
|
38 | // IPython.toolbar.add_button_group([ | |
|
39 | // {label:'my button', | |
|
40 | // icon:'ui-icon-disk', | |
|
41 | // callback:function(){alert('hoho'), | |
|
42 | // id : 'my_button_id', // this is optional | |
|
43 | // } | |
|
44 | // }, | |
|
45 | // {label:'my second button', | |
|
46 | // icon:'ui-icon-scissors', | |
|
47 | // callback:function(){alert('be carefull I cut')} | |
|
48 | // } | |
|
49 | // ], | |
|
50 | // "my_button_group_id" | |
|
51 | // ) | |
|
52 | // | |
|
53 | ToolBar.prototype.add_buttons_group = function (list, group_id) { | |
|
54 | var span_group = $('<span/>'); | |
|
55 | if( group_id != undefined ) { | |
|
56 | span_group.attr('id',group_id); | |
|
57 | } | |
|
58 | for(var el in list) { | |
|
59 | var button = $('<button/>').button({ | |
|
60 | icons : {primary : list[el].icon}, | |
|
61 | text : false, | |
|
62 | label : list[el].label | |
|
63 | }); | |
|
64 | var id = list[el].id; | |
|
65 | if( id != undefined ) | |
|
66 | button.attr('id',id); | |
|
67 | var fun = list[el].callback; | |
|
68 | button.click(fun); | |
|
69 | span_group.append(button); | |
|
70 | } | |
|
71 | span_group.buttonset(); | |
|
72 | $(this.selector).append(span_group); | |
|
73 | }; | |
|
23 | 74 | |
|
24 | 75 | ToolBar.prototype.style = function () { |
|
25 | 76 | this.element.addClass('border-box-sizing'). |
|
26 | addClass('ui-widget ui-widget-content'). | |
|
77 | addClass('ui-widget ui-widget-content toolbar'). | |
|
27 | 78 | css('border-top-style','none'). |
|
28 | 79 | css('border-left-style','none'). |
|
29 | 80 | css('border-right-style','none'); |
|
30 | this.element.find('#cell_type').addClass('ui-widget ui-widget-content'); | |
|
31 | this.element.find('#save_b').button({ | |
|
32 | icons : {primary: 'ui-icon-disk'}, | |
|
33 | text : false | |
|
34 | }); | |
|
35 | this.element.find('#cut_b').button({ | |
|
36 | icons: {primary: 'ui-icon-scissors'}, | |
|
37 | text : false | |
|
38 | }); | |
|
39 | this.element.find('#copy_b').button({ | |
|
40 | icons: {primary: 'ui-icon-copy'}, | |
|
41 | text : false | |
|
42 | }); | |
|
43 | this.element.find('#paste_b').button({ | |
|
44 | icons: {primary: 'ui-icon-clipboard'}, | |
|
45 | text : false | |
|
46 | }); | |
|
47 | this.element.find('#cut_copy_paste').buttonset(); | |
|
48 | this.element.find('#move_up_b').button({ | |
|
49 | icons: {primary: 'ui-icon-arrowthick-1-n'}, | |
|
50 | text : false | |
|
51 | }); | |
|
52 | this.element.find('#move_down_b').button({ | |
|
53 | icons: {primary: 'ui-icon-arrowthick-1-s'}, | |
|
54 | text : false | |
|
55 | }); | |
|
56 | this.element.find('#move_up_down').buttonset(); | |
|
57 | this.element.find('#insert_above_b').button({ | |
|
58 | icons: {primary: 'ui-icon-arrowthickstop-1-n'}, | |
|
59 | text : false | |
|
60 | }); | |
|
61 | this.element.find('#insert_below_b').button({ | |
|
62 | icons: {primary: 'ui-icon-arrowthickstop-1-s'}, | |
|
63 | text : false | |
|
64 | }); | |
|
65 | this.element.find('#insert_above_below').buttonset(); | |
|
66 | this.element.find('#run_b').button({ | |
|
67 | icons: {primary: 'ui-icon-play'}, | |
|
68 | text : false | |
|
69 | }); | |
|
70 | this.element.find('#interrupt_b').button({ | |
|
71 | icons: {primary: 'ui-icon-stop'}, | |
|
72 | text : false | |
|
73 | }); | |
|
74 | this.element.find('#run_int').buttonset(); | |
|
75 | }; | |
|
76 | ||
|
77 | ||
|
78 | ToolBar.prototype.bind_events = function () { | |
|
79 | var that = this; | |
|
80 | this.element.find('#save_b').click(function () { | |
|
81 | IPython.notebook.save_notebook(); | |
|
82 | }); | |
|
83 | this.element.find('#cut_b').click(function () { | |
|
84 | IPython.notebook.cut_cell(); | |
|
85 | }); | |
|
86 | this.element.find('#copy_b').click(function () { | |
|
87 | IPython.notebook.copy_cell(); | |
|
88 | }); | |
|
89 | this.element.find('#paste_b').click(function () { | |
|
90 | IPython.notebook.paste_cell(); | |
|
91 | }); | |
|
92 | this.element.find('#move_up_b').click(function () { | |
|
93 | IPython.notebook.move_cell_up(); | |
|
94 | }); | |
|
95 | this.element.find('#move_down_b').click(function () { | |
|
96 | IPython.notebook.move_cell_down(); | |
|
97 | }); | |
|
98 | this.element.find('#insert_above_b').click(function () { | |
|
99 | IPython.notebook.insert_cell_above('code'); | |
|
100 | }); | |
|
101 | this.element.find('#insert_below_b').click(function () { | |
|
102 | IPython.notebook.insert_cell_below('code'); | |
|
103 | }); | |
|
104 | this.element.find('#run_b').click(function () { | |
|
105 | IPython.notebook.execute_selected_cell(); | |
|
106 | }); | |
|
107 | this.element.find('#interrupt_b').click(function () { | |
|
108 | IPython.notebook.kernel.interrupt(); | |
|
109 | }); | |
|
110 | this.element.find('#cell_type').change(function () { | |
|
111 | var cell_type = $(this).val(); | |
|
112 | if (cell_type === 'code') { | |
|
113 | IPython.notebook.to_code(); | |
|
114 | } else if (cell_type === 'markdown') { | |
|
115 | IPython.notebook.to_markdown(); | |
|
116 | } else if (cell_type === 'raw') { | |
|
117 | IPython.notebook.to_raw(); | |
|
118 | } else if (cell_type === 'heading1') { | |
|
119 | IPython.notebook.to_heading(undefined, 1); | |
|
120 | } else if (cell_type === 'heading2') { | |
|
121 | IPython.notebook.to_heading(undefined, 2); | |
|
122 | } else if (cell_type === 'heading3') { | |
|
123 | IPython.notebook.to_heading(undefined, 3); | |
|
124 | } else if (cell_type === 'heading4') { | |
|
125 | IPython.notebook.to_heading(undefined, 4); | |
|
126 | } else if (cell_type === 'heading5') { | |
|
127 | IPython.notebook.to_heading(undefined, 5); | |
|
128 | } else if (cell_type === 'heading6') { | |
|
129 | IPython.notebook.to_heading(undefined, 6); | |
|
130 | }; | |
|
131 | }); | |
|
132 | $([IPython.events]).on('selected_cell_type_changed.Notebook', function (event, data) { | |
|
133 | if (data.cell_type === 'heading') { | |
|
134 | that.element.find('#cell_type').val(data.cell_type+data.level); | |
|
135 | } else { | |
|
136 | that.element.find('#cell_type').val(data.cell_type); | |
|
137 | } | |
|
138 | }); | |
|
139 | 81 | }; |
|
140 | 82 | |
|
141 | 83 | |
|
142 | 84 | ToolBar.prototype.toggle = function () { |
|
143 | 85 | this.element.toggle(); |
|
144 |
IPython.layout_manager |
|
|
86 | if (IPython.layout_manager != undefined) { | |
|
87 | IPython.layout_manager.do_resize(); | |
|
88 | } | |
|
145 | 89 | }; |
|
146 | 90 | |
|
147 | 91 |
@@ -104,13 +104,10 b' var IPython = (function (IPython) {' | |||
|
104 | 104 | // function that will be called if you press tab 1, 2, 3... times in a row |
|
105 | 105 | this.tabs_functions = [function (cell, text) { |
|
106 | 106 | that._request_tooltip(cell, text); |
|
107 | IPython.notification_widget.set_message('tab again to expand pager', 2500); | |
|
108 | 107 | }, function () { |
|
109 | 108 | that.expand(); |
|
110 | IPython.notification_widget.set_message('tab again to make pager sticky for 10s', 2500); | |
|
111 | 109 | }, function () { |
|
112 | 110 | that.stick(); |
|
113 | IPython.notification_widget.set_message('tab again to open help in pager', 2500); | |
|
114 | 111 | }, function (cell) { |
|
115 | 112 | that.cancel_stick(); |
|
116 | 113 | that.showInPager(cell); |
@@ -1,5 +1,5 b'' | |||
|
1 | 1 | //---------------------------------------------------------------------------- |
|
2 |
// Copyright (C) 2008-201 |
|
|
2 | // Copyright (C) 2008-2012 The IPython Development Team | |
|
3 | 3 | // |
|
4 | 4 | // Distributed under the terms of the BSD License. The full license is in |
|
5 | 5 | // the file COPYING, distributed as part of this software. |
@@ -13,6 +13,123 b" IPython.namespace('IPython.utils');" | |||
|
13 | 13 | |
|
14 | 14 | IPython.utils = (function (IPython) { |
|
15 | 15 | |
|
16 | //============================================================================ | |
|
17 | // Cross-browser RegEx Split | |
|
18 | //============================================================================ | |
|
19 | ||
|
20 | // This code has been MODIFIED from the code licensed below to not replace the | |
|
21 | // default browser split. The license is reproduced here. | |
|
22 | ||
|
23 | // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info: | |
|
24 | /*! | |
|
25 | * Cross-Browser Split 1.1.1 | |
|
26 | * Copyright 2007-2012 Steven Levithan <stevenlevithan.com> | |
|
27 | * Available under the MIT License | |
|
28 | * ECMAScript compliant, uniform cross-browser split method | |
|
29 | */ | |
|
30 | ||
|
31 | /** | |
|
32 | * Splits a string into an array of strings using a regex or string | |
|
33 | * separator. Matches of the separator are not included in the result array. | |
|
34 | * However, if `separator` is a regex that contains capturing groups, | |
|
35 | * backreferences are spliced into the result each time `separator` is | |
|
36 | * matched. Fixes browser bugs compared to the native | |
|
37 | * `String.prototype.split` and can be used reliably cross-browser. | |
|
38 | * @param {String} str String to split. | |
|
39 | * @param {RegExp|String} separator Regex or string to use for separating | |
|
40 | * the string. | |
|
41 | * @param {Number} [limit] Maximum number of items to include in the result | |
|
42 | * array. | |
|
43 | * @returns {Array} Array of substrings. | |
|
44 | * @example | |
|
45 | * | |
|
46 | * // Basic use | |
|
47 | * regex_split('a b c d', ' '); | |
|
48 | * // -> ['a', 'b', 'c', 'd'] | |
|
49 | * | |
|
50 | * // With limit | |
|
51 | * regex_split('a b c d', ' ', 2); | |
|
52 | * // -> ['a', 'b'] | |
|
53 | * | |
|
54 | * // Backreferences in result array | |
|
55 | * regex_split('..word1 word2..', /([a-z]+)(\d+)/i); | |
|
56 | * // -> ['..', 'word', '1', ' ', 'word', '2', '..'] | |
|
57 | */ | |
|
58 | var regex_split = function (str, separator, limit) { | |
|
59 | // If `separator` is not a regex, use `split` | |
|
60 | if (Object.prototype.toString.call(separator) !== "[object RegExp]") { | |
|
61 | return split.call(str, separator, limit); | |
|
62 | } | |
|
63 | var output = [], | |
|
64 | flags = (separator.ignoreCase ? "i" : "") + | |
|
65 | (separator.multiline ? "m" : "") + | |
|
66 | (separator.extended ? "x" : "") + // Proposed for ES6 | |
|
67 | (separator.sticky ? "y" : ""), // Firefox 3+ | |
|
68 | lastLastIndex = 0, | |
|
69 | // Make `global` and avoid `lastIndex` issues by working with a copy | |
|
70 | separator = new RegExp(separator.source, flags + "g"), | |
|
71 | separator2, match, lastIndex, lastLength; | |
|
72 | str += ""; // Type-convert | |
|
73 | ||
|
74 | compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined" | |
|
75 | if (!compliantExecNpcg) { | |
|
76 | // Doesn't need flags gy, but they don't hurt | |
|
77 | separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags); | |
|
78 | } | |
|
79 | /* Values for `limit`, per the spec: | |
|
80 | * If undefined: 4294967295 // Math.pow(2, 32) - 1 | |
|
81 | * If 0, Infinity, or NaN: 0 | |
|
82 | * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296; | |
|
83 | * If negative number: 4294967296 - Math.floor(Math.abs(limit)) | |
|
84 | * If other: Type-convert, then use the above rules | |
|
85 | */ | |
|
86 | limit = typeof(limit) === "undefined" ? | |
|
87 | -1 >>> 0 : // Math.pow(2, 32) - 1 | |
|
88 | limit >>> 0; // ToUint32(limit) | |
|
89 | while (match = separator.exec(str)) { | |
|
90 | // `separator.lastIndex` is not reliable cross-browser | |
|
91 | lastIndex = match.index + match[0].length; | |
|
92 | if (lastIndex > lastLastIndex) { | |
|
93 | output.push(str.slice(lastLastIndex, match.index)); | |
|
94 | // Fix browsers whose `exec` methods don't consistently return `undefined` for | |
|
95 | // nonparticipating capturing groups | |
|
96 | if (!compliantExecNpcg && match.length > 1) { | |
|
97 | match[0].replace(separator2, function () { | |
|
98 | for (var i = 1; i < arguments.length - 2; i++) { | |
|
99 | if (typeof(arguments[i]) === "undefined") { | |
|
100 | match[i] = undefined; | |
|
101 | } | |
|
102 | } | |
|
103 | }); | |
|
104 | } | |
|
105 | if (match.length > 1 && match.index < str.length) { | |
|
106 | Array.prototype.push.apply(output, match.slice(1)); | |
|
107 | } | |
|
108 | lastLength = match[0].length; | |
|
109 | lastLastIndex = lastIndex; | |
|
110 | if (output.length >= limit) { | |
|
111 | break; | |
|
112 | } | |
|
113 | } | |
|
114 | if (separator.lastIndex === match.index) { | |
|
115 | separator.lastIndex++; // Avoid an infinite loop | |
|
116 | } | |
|
117 | } | |
|
118 | if (lastLastIndex === str.length) { | |
|
119 | if (lastLength || !separator.test("")) { | |
|
120 | output.push(""); | |
|
121 | } | |
|
122 | } else { | |
|
123 | output.push(str.slice(lastLastIndex)); | |
|
124 | } | |
|
125 | return output.length > limit ? output.slice(0, limit) : output; | |
|
126 | }; | |
|
127 | ||
|
128 | //============================================================================ | |
|
129 | // End contributed Cross-browser RegEx Split | |
|
130 | //============================================================================ | |
|
131 | ||
|
132 | ||
|
16 | 133 | var uuid = function () { |
|
17 | 134 | // http://www.ietf.org/rfc/rfc4122.txt |
|
18 | 135 | var s = []; |
@@ -78,11 +195,30 b' IPython.utils = (function (IPython) {' | |||
|
78 | 195 | tmp = txt; |
|
79 | 196 | do { |
|
80 | 197 | txt = tmp; |
|
81 |
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 | |
|
82 | 200 | } while (tmp.length < txt.length); |
|
83 | 201 | return txt; |
|
84 | 202 | } |
|
85 | 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 | ||
|
86 | 222 | grow = function(element) { |
|
87 | 223 | // Grow the cell by hand. This is used upon reloading from JSON, when the |
|
88 | 224 | // autogrow handler is not called. |
@@ -138,11 +274,14 b' IPython.utils = (function (IPython) {' | |||
|
138 | 274 | |
|
139 | 275 | |
|
140 | 276 | return { |
|
277 | regex_split : regex_split, | |
|
141 | 278 | uuid : uuid, |
|
142 | 279 | fixConsole : fixConsole, |
|
143 | 280 | keycodes : keycodes, |
|
144 | 281 | grow : grow, |
|
145 | 282 | fixCarriageReturn : fixCarriageReturn, |
|
283 | wrapUrls : wrapUrls, | |
|
284 | autoLinkUrls : autoLinkUrls, | |
|
146 | 285 | points_to_pixels : points_to_pixels |
|
147 | 286 | }; |
|
148 | 287 |
@@ -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 | {% endif %} |
|
8 | 8 | <script type="text/javascript"> |
|
9 | 9 | // MathJax disabled, set as null to distingish from *missing* MathJax, |
@@ -74,9 +74,9 b' data-notebook-id={{notebook_id}}' | |||
|
74 | 74 | <ul> |
|
75 | 75 | <li id="cut_cell"><a href="#">Cut Cell</a></li> |
|
76 | 76 | <li id="copy_cell"><a href="#">Copy Cell</a></li> |
|
77 | <li id="paste_cell" class="ui-state-disabled"><a href="#">Paste Cell</a></li> | |
|
78 | 77 | <li id="paste_cell_above" class="ui-state-disabled"><a href="#">Paste Cell Above</a></li> |
|
79 | 78 | <li id="paste_cell_below" class="ui-state-disabled"><a href="#">Paste Cell Below</a></li> |
|
79 | <li id="paste_cell_replace" class="ui-state-disabled"><a href="#">Paste Cell & Replace</a></li> | |
|
80 | 80 | <li id="delete_cell"><a href="#">Delete</a></li> |
|
81 | 81 | <hr/> |
|
82 | 82 | <li id="split_cell"><a href="#">Split Cell</a></li> |
@@ -107,6 +107,8 b' data-notebook-id={{notebook_id}}' | |||
|
107 | 107 | <li id="run_cell"><a href="#">Run</a></li> |
|
108 | 108 | <li id="run_cell_in_place"><a href="#">Run in Place</a></li> |
|
109 | 109 | <li id="run_all_cells"><a href="#">Run All</a></li> |
|
110 | <li id="run_all_cells_above"><a href="#">Run All Above</a></li> | |
|
111 | <li id="run_all_cells_below"><a href="#">Run All Below</a></li> | |
|
110 | 112 | <hr/> |
|
111 | 113 | <li id="to_code"><a href="#">Code</a></li> |
|
112 | 114 | <li id="to_markdown"><a href="#">Markdown </a></li> |
@@ -156,54 +158,22 b' data-notebook-id={{notebook_id}}' | |||
|
156 | 158 | </div> |
|
157 | 159 | |
|
158 | 160 | |
|
159 | <div id="toolbar"> | |
|
160 | ||
|
161 | <span> | |
|
162 | <button id="save_b">Save</button> | |
|
163 | </span> | |
|
164 | <span id="cut_copy_paste"> | |
|
165 | <button id="cut_b" title="Cut Cell">Cut Cell</button> | |
|
166 | <button id="copy_b" title="Copy Cell">Copy Cell</button> | |
|
167 | <button id="paste_b" title="Paste Cell">Paste Cell</button> | |
|
168 | </span> | |
|
169 | <span id="move_up_down"> | |
|
170 | <button id="move_up_b" title="Move Cell Up">Move Cell Up</button> | |
|
171 | <button id="move_down_b" title="Move Cell Down">Move Down</button> | |
|
172 | </span> | |
|
173 | <span id="insert_above_below"> | |
|
174 | <button id="insert_above_b" title="Insert Cell Above">Insert Cell Above</button> | |
|
175 | <button id="insert_below_b" title="Insert Cell Below">Insert Cell Below</button> | |
|
176 | </span> | |
|
177 | <span id="run_int"> | |
|
178 | <button id="run_b" title="Run Cell">Run Cell</button> | |
|
179 | <button id="interrupt_b" title="Interrupt">Interrupt</button> | |
|
180 | </span> | |
|
181 | <span> | |
|
182 | <select id="cell_type"> | |
|
183 | <option value="code">Code</option> | |
|
184 | <option value="markdown">Markdown</option> | |
|
185 | <option value="raw">Raw Text</option> | |
|
186 | <option value="heading1">Heading 1</option> | |
|
187 | <option value="heading2">Heading 2</option> | |
|
188 | <option value="heading3">Heading 3</option> | |
|
189 | <option value="heading4">Heading 4</option> | |
|
190 | <option value="heading5">Heading 5</option> | |
|
191 | <option value="heading6">Heading 6</option> | |
|
192 | </select> | |
|
193 | </span> | |
|
194 | ||
|
195 | </div> | |
|
161 | <div id="maintoolbar"></div> | |
|
196 | 162 | |
|
197 | 163 | <div id="main_app"> |
|
198 | 164 | |
|
199 | 165 | <div id="notebook_panel"> |
|
200 | 166 | <div id="notebook"></div> |
|
201 | 167 | <div id="pager_splitter"></div> |
|
202 |
<div id="pager |
|
|
168 | <div id="pager_container"> | |
|
169 | <div id='pager_button_area'> | |
|
170 | </div> | |
|
171 | <div id="pager"></div> | |
|
172 | </div> | |
|
203 | 173 | </div> |
|
204 | 174 | |
|
205 | 175 | </div> |
|
206 | <div id='tooltip' class='tooltip ui-corner-all' style='display:none'></div> | |
|
176 | <div id='tooltip' class='ipython_tooltip ui-corner-all' style='display:none'></div> | |
|
207 | 177 | |
|
208 | 178 | |
|
209 | 179 | {% endblock %} |
@@ -212,6 +182,8 b' data-notebook-id={{notebook_id}}' | |||
|
212 | 182 | {% block script %} |
|
213 | 183 | |
|
214 | 184 | <script src="{{ static_url("codemirror/lib/codemirror.js") }}" charset="utf-8"></script> |
|
185 | <script src="{{ static_url("codemirror/lib/util/loadmode.js") }}" charset="utf-8"></script> | |
|
186 | <script src="{{ static_url("codemirror/lib/util/multiplex.js") }}" charset="utf-8"></script> | |
|
215 | 187 | <script src="{{ static_url("codemirror/mode/python/python.js") }}" charset="utf-8"></script> |
|
216 | 188 | <script src="{{ static_url("codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script> |
|
217 | 189 | <script src="{{ static_url("codemirror/mode/xml/xml.js") }}" charset="utf-8"></script> |
@@ -228,7 +200,7 b' data-notebook-id={{notebook_id}}' | |||
|
228 | 200 | <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script> |
|
229 | 201 | <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script> |
|
230 | 202 | <script src="{{ static_url("js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script> |
|
231 |
<script src="{{ static_url("js/ |
|
|
203 | <script src="{{ static_url("js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script> | |
|
232 | 204 | <script src="{{ static_url("js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script> |
|
233 | 205 | <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script> |
|
234 | 206 | <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script> |
@@ -240,10 +212,12 b' data-notebook-id={{notebook_id}}' | |||
|
240 | 212 | <script src="{{ static_url("js/pager.js") }}" type="text/javascript" charset="utf-8"></script> |
|
241 | 213 | <script src="{{ static_url("js/menubar.js") }}" type="text/javascript" charset="utf-8"></script> |
|
242 | 214 | <script src="{{ static_url("js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script> |
|
215 | <script src="{{ static_url("js/maintoolbar.js") }}" type="text/javascript" charset="utf-8"></script> | |
|
243 | 216 | <script src="{{ static_url("js/notebook.js") }}" type="text/javascript" charset="utf-8"></script> |
|
244 | 217 | <script src="{{ static_url("js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script> |
|
245 | 218 | <script src="{{ static_url("js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script> |
|
246 | 219 | <script src="{{ static_url("js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script> |
|
220 | <script src="{{ static_url("js/config.js") }}" type="text/javascript" charset="utf-8"></script> | |
|
247 | 221 | <script src="{{ static_url("js/notebookmain.js") }}" type="text/javascript" charset="utf-8"></script> |
|
248 | 222 | |
|
249 | 223 | <script src="{{ static_url("js/contexthint.js") }}" charset="utf-8"></script> |
@@ -28,7 +28,7 b'' | |||
|
28 | 28 | <body {% block params %}{% endblock %}> |
|
29 | 29 | |
|
30 | 30 | <div id="header"> |
|
31 |
<span id="ipython_notebook">< |
|
|
31 | <span id="ipython_notebook"><div><a href={{base_project_url}} alt='dashboard'><img src='{{static_url("ipynblogo.png") }}' alt='IPython Notebook'/></a></div></span> | |
|
32 | 32 | |
|
33 | 33 | {% block login_widget %} |
|
34 | 34 |
@@ -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 | {% endif %} |
|
8 | 8 | <script type="text/javascript"> |
|
9 | 9 | // MathJax disabled, set as null to distingish from *missing* MathJax, |
@@ -69,7 +69,7 b' data-notebook-id={{notebook_id}}' | |||
|
69 | 69 | |
|
70 | 70 | <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script> |
|
71 | 71 | <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script> |
|
72 |
<script src="{{ static_url("js/ |
|
|
72 | <script src="{{ static_url("js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script> | |
|
73 | 73 | <script src="{{ static_url("js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script> |
|
74 | 74 | <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script> |
|
75 | 75 | <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script> |
@@ -10,6 +10,7 b' import re' | |||
|
10 | 10 | import sys |
|
11 | 11 | from textwrap import dedent |
|
12 | 12 | from unicodedata import category |
|
13 | import webbrowser | |
|
13 | 14 | |
|
14 | 15 | # System library imports |
|
15 | 16 | from IPython.external.qt import QtCore, QtGui |
@@ -267,7 +268,6 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||
|
267 | 268 | self._continuation_prompt = '> ' |
|
268 | 269 | self._continuation_prompt_html = None |
|
269 | 270 | self._executing = False |
|
270 | self._filter_drag = False | |
|
271 | 271 | self._filter_resize = False |
|
272 | 272 | self._html_exporter = HtmlExporter(self._control) |
|
273 | 273 | self._input_buffer_executing = '' |
@@ -342,7 +342,51 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||
|
342 | 342 | triggered=self.reset_font) |
|
343 | 343 | self.addAction(self.reset_font_size) |
|
344 | 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) | |
|
345 | 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) | |
|
346 | 390 | |
|
347 | 391 | def eventFilter(self, obj, event): |
|
348 | 392 | """ Reimplemented to ensure a console-like behavior in the underlying |
@@ -391,39 +435,6 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||
|
391 | 435 | event.key() in self._shortcuts: |
|
392 | 436 | event.accept() |
|
393 | 437 | |
|
394 | # Ensure that drags are safe. The problem is that the drag starting | |
|
395 | # logic, which determines whether the drag is a Copy or Move, is locked | |
|
396 | # down in QTextControl. If the widget is editable, which it must be if | |
|
397 | # we're not executing, the drag will be a Move. The following hack | |
|
398 | # prevents QTextControl from deleting the text by clearing the selection | |
|
399 | # when a drag leave event originating from this widget is dispatched. | |
|
400 | # The fact that we have to clear the user's selection is unfortunate, | |
|
401 | # but the alternative--trying to prevent Qt from using its hardwired | |
|
402 | # drag logic and writing our own--is worse. | |
|
403 | elif etype == QtCore.QEvent.DragEnter and \ | |
|
404 | obj == self._control.viewport() and \ | |
|
405 | event.source() == self._control.viewport(): | |
|
406 | self._filter_drag = True | |
|
407 | elif etype == QtCore.QEvent.DragLeave and \ | |
|
408 | obj == self._control.viewport() and \ | |
|
409 | self._filter_drag: | |
|
410 | cursor = self._control.textCursor() | |
|
411 | cursor.clearSelection() | |
|
412 | self._control.setTextCursor(cursor) | |
|
413 | self._filter_drag = False | |
|
414 | ||
|
415 | # Ensure that drops are safe. | |
|
416 | elif etype == QtCore.QEvent.Drop and obj == self._control.viewport(): | |
|
417 | cursor = self._control.cursorForPosition(event.pos()) | |
|
418 | if self._in_buffer(cursor.position()): | |
|
419 | text = event.mimeData().text() | |
|
420 | self._insert_plain_text_into_buffer(cursor, text) | |
|
421 | ||
|
422 | # Qt is expecting to get something here--drag and drop occurs in its | |
|
423 | # own event loop. Send a DragLeave event to end it. | |
|
424 | QtGui.qApp.sendEvent(obj, QtGui.QDragLeaveEvent()) | |
|
425 | return True | |
|
426 | ||
|
427 | 438 | # Handle scrolling of the vsplit pager. This hack attempts to solve |
|
428 | 439 | # problems with tearing of the help text inside the pager window. This |
|
429 | 440 | # happens only on Mac OS X with both PySide and PyQt. This fix isn't |
@@ -432,6 +443,11 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||
|
432 | 443 | obj == self._page_control: |
|
433 | 444 | self._page_control.repaint() |
|
434 | 445 | return True |
|
446 | ||
|
447 | elif etype == QtCore.QEvent.MouseMove: | |
|
448 | anchor = self._control.anchorAt(event.pos()) | |
|
449 | QtGui.QToolTip.showText(event.globalPos(), anchor) | |
|
450 | ||
|
435 | 451 | return super(ConsoleWidget, self).eventFilter(obj, event) |
|
436 | 452 | |
|
437 | 453 | #--------------------------------------------------------------------------- |
@@ -511,6 +527,11 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||
|
511 | 527 | """ |
|
512 | 528 | self.layout().currentWidget().copy() |
|
513 | 529 | |
|
530 | def copy_anchor(self, anchor): | |
|
531 | """ Copy anchor text to the clipboard | |
|
532 | """ | |
|
533 | QtGui.QApplication.clipboard().setText(anchor) | |
|
534 | ||
|
514 | 535 | def cut(self): |
|
515 | 536 | """ Copy the currently selected text to the clipboard and delete it |
|
516 | 537 | if it's inside the input buffer. |
@@ -678,6 +699,11 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||
|
678 | 699 | |
|
679 | 700 | font = property(_get_font, _set_font) |
|
680 | 701 | |
|
702 | def open_anchor(self, anchor): | |
|
703 | """ Open selected anchor in the default webbrowser | |
|
704 | """ | |
|
705 | webbrowser.open( anchor ) | |
|
706 | ||
|
681 | 707 | def paste(self, mode=QtGui.QClipboard.Clipboard): |
|
682 | 708 | """ Paste the contents of the clipboard into the input region. |
|
683 | 709 | |
@@ -858,6 +884,11 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||
|
858 | 884 | |
|
859 | 885 | return result |
|
860 | 886 | |
|
887 | def _append_block(self, block_format=None, before_prompt=False): | |
|
888 | """ Appends an new QTextBlock to the end of the console buffer. | |
|
889 | """ | |
|
890 | self._append_custom(self._insert_block, block_format, before_prompt) | |
|
891 | ||
|
861 | 892 | def _append_html(self, html, before_prompt=False): |
|
862 | 893 | """ Appends HTML at the end of the console buffer. |
|
863 | 894 | """ |
@@ -966,6 +997,14 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||
|
966 | 997 | self.paste_action.setEnabled(self.can_paste()) |
|
967 | 998 | self.paste_action.setShortcut(QtGui.QKeySequence.Paste) |
|
968 | 999 | |
|
1000 | anchor = self._control.anchorAt(pos) | |
|
1001 | if anchor: | |
|
1002 | menu.addSeparator() | |
|
1003 | self.copy_link_action = menu.addAction( | |
|
1004 | 'Copy Link Address', lambda: self.copy_anchor(anchor=anchor)) | |
|
1005 | self.open_link_action = menu.addAction( | |
|
1006 | 'Open Link', lambda: self.open_anchor(anchor=anchor)) | |
|
1007 | ||
|
969 | 1008 | menu.addSeparator() |
|
970 | 1009 | menu.addAction(self.select_all_action) |
|
971 | 1010 | |
@@ -1004,9 +1043,14 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||
|
1004 | 1043 | elif self.kind == 'rich': |
|
1005 | 1044 | control = QtGui.QTextEdit() |
|
1006 | 1045 | control.setAcceptRichText(False) |
|
1046 | control.setMouseTracking(True) | |
|
1047 | ||
|
1048 | # Prevent the widget from handling drops, as we already provide | |
|
1049 | # the logic in this class. | |
|
1050 | control.setAcceptDrops(False) | |
|
1007 | 1051 | |
|
1008 | 1052 | # Install event filters. The filter on the viewport is needed for |
|
1009 |
# mouse events |
|
|
1053 | # mouse events. | |
|
1010 | 1054 | control.installEventFilter(self) |
|
1011 | 1055 | control.viewport().installEventFilter(self) |
|
1012 | 1056 | |
@@ -1545,6 +1589,13 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||
|
1545 | 1589 | self._continuation_prompt = self._insert_html_fetching_plain_text( |
|
1546 | 1590 | cursor, self._continuation_prompt_html) |
|
1547 | 1591 | |
|
1592 | def _insert_block(self, cursor, block_format=None): | |
|
1593 | """ Inserts an empty QTextBlock using the specified cursor. | |
|
1594 | """ | |
|
1595 | if block_format is None: | |
|
1596 | block_format = QtGui.QTextBlockFormat() | |
|
1597 | cursor.insertBlock(block_format) | |
|
1598 | ||
|
1548 | 1599 | def _insert_html(self, cursor, html): |
|
1549 | 1600 | """ Inserts HTML using the specified cursor in such a way that future |
|
1550 | 1601 | formatting is unaffected. |
@@ -1736,6 +1787,32 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||
|
1736 | 1787 | else: |
|
1737 | 1788 | self._append_plain_text(text) |
|
1738 | 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 | ||
|
1739 | 1816 | def _prompt_finished(self): |
|
1740 | 1817 | """ Called immediately after a prompt is finished, i.e. when some input |
|
1741 | 1818 | will be processed and a new prompt displayed. |
@@ -1866,7 +1943,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||
|
1866 | 1943 | cursor.movePosition(QtGui.QTextCursor.Left, |
|
1867 | 1944 | QtGui.QTextCursor.KeepAnchor) |
|
1868 | 1945 | if cursor.selection().toPlainText() != '\n': |
|
1869 |
self._append_ |
|
|
1946 | self._append_block() | |
|
1870 | 1947 | |
|
1871 | 1948 | # Write the prompt. |
|
1872 | 1949 | self._append_plain_text(self._prompt_sep) |
@@ -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 |
@@ -2,7 +2,7 b'' | |||
|
2 | 2 | import unittest |
|
3 | 3 | |
|
4 | 4 | # System library imports |
|
5 | from IPython.external.qt import QtGui | |
|
5 | from IPython.external.qt import QtCore, QtGui | |
|
6 | 6 | |
|
7 | 7 | # Local imports |
|
8 | 8 | from IPython.frontend.qt.console.console_widget import ConsoleWidget |
@@ -40,3 +40,41 b' class TestConsoleWidget(unittest.TestCase):' | |||
|
40 | 40 | self.assertEqual(expected_outputs[i], selection) |
|
41 | 41 | # clear all the text |
|
42 | 42 | cursor.insertText('') |
|
43 | ||
|
44 | def test_link_handling(self): | |
|
45 | noKeys = QtCore.Qt | |
|
46 | noButton = QtCore.Qt.MouseButton(0) | |
|
47 | noButtons = QtCore.Qt.MouseButtons(0) | |
|
48 | noModifiers = QtCore.Qt.KeyboardModifiers(0) | |
|
49 | MouseMove = QtCore.QEvent.MouseMove | |
|
50 | QMouseEvent = QtGui.QMouseEvent | |
|
51 | ||
|
52 | w = ConsoleWidget() | |
|
53 | cursor = w._get_prompt_cursor() | |
|
54 | w._insert_html(cursor, '<a href="http://python.org">written in</a>') | |
|
55 | obj = w._control | |
|
56 | tip = QtGui.QToolTip | |
|
57 | self.assertEqual(tip.text(), u'') | |
|
58 | ||
|
59 | # should be somewhere else | |
|
60 | elsewhereEvent = QMouseEvent(MouseMove, QtCore.QPoint(50,50), | |
|
61 | noButton, noButtons, noModifiers) | |
|
62 | w.eventFilter(obj, elsewhereEvent) | |
|
63 | self.assertEqual(tip.isVisible(), False) | |
|
64 | self.assertEqual(tip.text(), u'') | |
|
65 | ||
|
66 | #self.assertEqual(tip.text(), u'') | |
|
67 | # should be over text | |
|
68 | overTextEvent = QMouseEvent(MouseMove, QtCore.QPoint(1,5), | |
|
69 | noButton, noButtons, noModifiers) | |
|
70 | w.eventFilter(obj, overTextEvent) | |
|
71 | self.assertEqual(tip.isVisible(), True) | |
|
72 | self.assertEqual(tip.text(), "http://python.org") | |
|
73 | ||
|
74 | # should still be over text | |
|
75 | stillOverTextEvent = QMouseEvent(MouseMove, QtCore.QPoint(1,5), | |
|
76 | noButton, noButtons, noModifiers) | |
|
77 | w.eventFilter(obj, stillOverTextEvent) | |
|
78 | self.assertEqual(tip.isVisible(), True) | |
|
79 | self.assertEqual(tip.text(), "http://python.org") | |
|
80 |
@@ -19,15 +19,26 b' from __future__ import print_function' | |||
|
19 | 19 | |
|
20 | 20 | import bdb |
|
21 | 21 | import signal |
|
22 | import os | |
|
22 | 23 | import sys |
|
23 | 24 | import time |
|
25 | import subprocess | |
|
26 | from io import BytesIO | |
|
27 | import base64 | |
|
24 | 28 | |
|
25 | 29 | from Queue import Empty |
|
26 | 30 | |
|
31 | try: | |
|
32 | from contextlib import nested | |
|
33 | except: | |
|
34 | from IPython.utils.nested_context import nested | |
|
35 | ||
|
27 | 36 | from IPython.core.alias import AliasManager, AliasError |
|
28 | 37 | from IPython.core import page |
|
29 | 38 | from IPython.utils.warn import warn, error, fatal |
|
30 | 39 | from IPython.utils import io |
|
40 | from IPython.utils.traitlets import List, Enum, Any | |
|
41 | from IPython.utils.tempdir import NamedFileInTemporaryDirectory | |
|
31 | 42 | |
|
32 | 43 | from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell |
|
33 | 44 | from IPython.frontend.terminal.console.completer import ZMQCompleter |
@@ -36,7 +47,64 b' from IPython.frontend.terminal.console.completer import ZMQCompleter' | |||
|
36 | 47 | class ZMQTerminalInteractiveShell(TerminalInteractiveShell): |
|
37 | 48 | """A subclass of TerminalInteractiveShell that uses the 0MQ kernel""" |
|
38 | 49 | _executing = False |
|
39 | ||
|
50 | ||
|
51 | image_handler = Enum(('PIL', 'stream', 'tempfile', 'callable'), | |
|
52 | config=True, help= | |
|
53 | """ | |
|
54 | Handler for image type output. This is useful, for example, | |
|
55 | when connecting to the kernel in which pylab inline backend is | |
|
56 | activated. There are four handlers defined. 'PIL': Use | |
|
57 | Python Imaging Library to popup image; 'stream': Use an | |
|
58 | external program to show the image. Image will be fed into | |
|
59 | the STDIN of the program. You will need to configure | |
|
60 | `stream_image_handler`; 'tempfile': Use an external program to | |
|
61 | show the image. Image will be saved in a temporally file and | |
|
62 | the program is called with the temporally file. You will need | |
|
63 | to configure `tempfile_image_handler`; 'callable': You can set | |
|
64 | any Python callable which is called with the image data. You | |
|
65 | will need to configure `callable_image_handler`. | |
|
66 | """ | |
|
67 | ) | |
|
68 | ||
|
69 | stream_image_handler = List(config=True, help= | |
|
70 | """ | |
|
71 | Command to invoke an image viewer program when you are using | |
|
72 | 'stream' image handler. This option is a list of string where | |
|
73 | the first element is the command itself and reminders are the | |
|
74 | options for the command. Raw image data is given as STDIN to | |
|
75 | the program. | |
|
76 | """ | |
|
77 | ) | |
|
78 | ||
|
79 | tempfile_image_handler = List(config=True, help= | |
|
80 | """ | |
|
81 | Command to invoke an image viewer program when you are using | |
|
82 | 'tempfile' image handler. This option is a list of string | |
|
83 | where the first element is the command itself and reminders | |
|
84 | are the options for the command. You can use {file} and | |
|
85 | {format} in the string to represent the location of the | |
|
86 | generated image file and image format. | |
|
87 | """ | |
|
88 | ) | |
|
89 | ||
|
90 | callable_image_handler = Any(config=True, help= | |
|
91 | """ | |
|
92 | Callable object called via 'callable' image handler with one | |
|
93 | argument, `data`, which is `msg["content"]["data"]` where | |
|
94 | `msg` is the message from iopub channel. For exmaple, you can | |
|
95 | find base64 encoded PNG data as `data['image/png']`. | |
|
96 | """ | |
|
97 | ) | |
|
98 | ||
|
99 | mime_preference = List( | |
|
100 | default_value=['image/png', 'image/jpeg', 'image/svg+xml'], | |
|
101 | config=True, allow_none=False, help= | |
|
102 | """ | |
|
103 | Preferred object representation MIME type in order. First | |
|
104 | matched MIME type will be used. | |
|
105 | """ | |
|
106 | ) | |
|
107 | ||
|
40 | 108 | def __init__(self, *args, **kwargs): |
|
41 | 109 | self.km = kwargs.pop('kernel_manager') |
|
42 | 110 | self.session_id = self.km.session.session |
@@ -163,6 +231,7 b' class ZMQTerminalInteractiveShell(TerminalInteractiveShell):' | |||
|
163 | 231 | elif msg_type == 'pyout': |
|
164 | 232 | self.execution_count = int(sub_msg["content"]["execution_count"]) |
|
165 | 233 | format_dict = sub_msg["content"]["data"] |
|
234 | self.handle_rich_data(format_dict) | |
|
166 | 235 | # taken from DisplayHook.__call__: |
|
167 | 236 | hook = self.displayhook |
|
168 | 237 | hook.start_displayhook() |
@@ -171,6 +240,61 b' class ZMQTerminalInteractiveShell(TerminalInteractiveShell):' | |||
|
171 | 240 | hook.log_output(format_dict) |
|
172 | 241 | hook.finish_displayhook() |
|
173 | 242 | |
|
243 | elif msg_type == 'display_data': | |
|
244 | self.handle_rich_data(sub_msg["content"]["data"]) | |
|
245 | ||
|
246 | _imagemime = { | |
|
247 | 'image/png': 'png', | |
|
248 | 'image/jpeg': 'jpeg', | |
|
249 | 'image/svg+xml': 'svg', | |
|
250 | } | |
|
251 | ||
|
252 | def handle_rich_data(self, data): | |
|
253 | for mime in self.mime_preference: | |
|
254 | if mime in data and mime in self._imagemime: | |
|
255 | self.handle_image(data, mime) | |
|
256 | return | |
|
257 | ||
|
258 | def handle_image(self, data, mime): | |
|
259 | handler = getattr( | |
|
260 | self, 'handle_image_{0}'.format(self.image_handler), None) | |
|
261 | if handler: | |
|
262 | handler(data, mime) | |
|
263 | ||
|
264 | def handle_image_PIL(self, data, mime): | |
|
265 | if mime not in ('image/png', 'image/jpeg'): | |
|
266 | return | |
|
267 | import PIL.Image | |
|
268 | raw = base64.decodestring(data[mime].encode('ascii')) | |
|
269 | img = PIL.Image.open(BytesIO(raw)) | |
|
270 | img.show() | |
|
271 | ||
|
272 | def handle_image_stream(self, data, mime): | |
|
273 | raw = base64.decodestring(data[mime].encode('ascii')) | |
|
274 | imageformat = self._imagemime[mime] | |
|
275 | fmt = dict(format=imageformat) | |
|
276 | args = [s.format(**fmt) for s in self.stream_image_handler] | |
|
277 | with open(os.devnull, 'w') as devnull: | |
|
278 | proc = subprocess.Popen( | |
|
279 | args, stdin=subprocess.PIPE, | |
|
280 | stdout=devnull, stderr=devnull) | |
|
281 | proc.communicate(raw) | |
|
282 | ||
|
283 | def handle_image_tempfile(self, data, mime): | |
|
284 | raw = base64.decodestring(data[mime].encode('ascii')) | |
|
285 | imageformat = self._imagemime[mime] | |
|
286 | filename = 'tmp.{0}'.format(imageformat) | |
|
287 | with nested(NamedFileInTemporaryDirectory(filename), | |
|
288 | open(os.devnull, 'w')) as (f, devnull): | |
|
289 | f.write(raw) | |
|
290 | f.flush() | |
|
291 | fmt = dict(file=f.name, format=imageformat) | |
|
292 | args = [s.format(**fmt) for s in self.tempfile_image_handler] | |
|
293 | subprocess.call(args, stdout=devnull, stderr=devnull) | |
|
294 | ||
|
295 | def handle_image_callable(self, data, mime): | |
|
296 | self.callable_image_handler(data) | |
|
297 | ||
|
174 | 298 | def handle_stdin_request(self, timeout=0.1): |
|
175 | 299 | """ Method to capture raw_input |
|
176 | 300 | """ |
@@ -264,8 +264,6 b' class InteractiveShellEmbed(TerminalInteractiveShell):' | |||
|
264 | 264 | self.user_ns = orig_user_ns |
|
265 | 265 | self.compile.flags = orig_compile_flags |
|
266 | 266 | |
|
267 | _embedded_shell = None | |
|
268 | ||
|
269 | 267 | |
|
270 | 268 | def embed(**kwargs): |
|
271 | 269 | """Call this to embed IPython at the current point in your program. |
@@ -284,7 +282,7 b' def embed(**kwargs):' | |||
|
284 | 282 | d = 40 |
|
285 | 283 | embed |
|
286 | 284 | |
|
287 |
Full customization can be done by passing a :class:` |
|
|
285 | Full customization can be done by passing a :class:`Config` in as the | |
|
288 | 286 | config argument. |
|
289 | 287 | """ |
|
290 | 288 | config = kwargs.get('config') |
@@ -294,7 +292,5 b' def embed(**kwargs):' | |||
|
294 | 292 | config = load_default_config() |
|
295 | 293 | config.InteractiveShellEmbed = config.TerminalInteractiveShell |
|
296 | 294 | kwargs['config'] = config |
|
297 | global _embedded_shell | |
|
298 | if _embedded_shell is None: | |
|
299 | _embedded_shell = InteractiveShellEmbed(**kwargs) | |
|
300 | _embedded_shell(header=header, stack_depth=2, compile_flags=compile_flags) | |
|
295 | shell = InteractiveShellEmbed.instance(**kwargs) | |
|
296 | shell(header=header, stack_depth=2, compile_flags=compile_flags) |
@@ -357,7 +357,7 b' class TerminalInteractiveShell(InteractiveShell):' | |||
|
357 | 357 | usage=None, banner1=None, banner2=None, display_banner=None): |
|
358 | 358 | |
|
359 | 359 | super(TerminalInteractiveShell, self).__init__( |
|
360 | config=config, profile_dir=profile_dir, user_ns=user_ns, | |
|
360 | config=config, ipython_dir=ipython_dir, profile_dir=profile_dir, user_ns=user_ns, | |
|
361 | 361 | user_module=user_module, custom_exceptions=custom_exceptions |
|
362 | 362 | ) |
|
363 | 363 | # use os.system instead of utils.process.system by default, |
@@ -5,7 +5,7 b' import subprocess' | |||
|
5 | 5 | import sys |
|
6 | 6 | |
|
7 | 7 | from IPython.core.error import TryNext |
|
8 | ||
|
8 | import IPython.utils.py3compat as py3compat | |
|
9 | 9 | |
|
10 | 10 | def win32_clipboard_get(): |
|
11 | 11 | """ Get the current clipboard's text on Windows. |
@@ -31,6 +31,7 b' def osx_clipboard_get():' | |||
|
31 | 31 | text, stderr = p.communicate() |
|
32 | 32 | # Text comes in with old Mac \r line endings. Change them to \n. |
|
33 | 33 | text = text.replace('\r', '\n') |
|
34 | text = py3compat.cast_unicode(text, py3compat.DEFAULT_ENCODING) | |
|
34 | 35 | return text |
|
35 | 36 | |
|
36 | 37 | def tkinter_clipboard_get(): |
@@ -49,6 +50,7 b' def tkinter_clipboard_get():' | |||
|
49 | 50 | root.withdraw() |
|
50 | 51 | text = root.clipboard_get() |
|
51 | 52 | root.destroy() |
|
53 | text = py3compat.cast_unicode(text, py3compat.DEFAULT_ENCODING) | |
|
52 | 54 | return text |
|
53 | 55 | |
|
54 | 56 |
@@ -1,3 +1,6 b'' | |||
|
1 | from __future__ import unicode_literals | |
|
2 | ||
|
3 | ||
|
1 | 4 | """Module for interactive demos using IPython. |
|
2 | 5 | |
|
3 | 6 | This module implements a few classes for running Python scripts interactively |
@@ -179,7 +182,7 b' from IPython.utils.PyColorize import Parser' | |||
|
179 | 182 | from IPython.utils import io |
|
180 | 183 | from IPython.utils.io import file_read, file_readlines |
|
181 | 184 | from IPython.utils.text import marquee |
|
182 | ||
|
185 | from IPython.utils import openpy | |
|
183 | 186 | __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError'] |
|
184 | 187 | |
|
185 | 188 | class DemoError(Exception): pass |
@@ -264,13 +267,13 b' class Demo(object):' | |||
|
264 | 267 | self.fobj = self.src |
|
265 | 268 | else: |
|
266 | 269 | # Assume it's a string or something that can be converted to one |
|
267 | self.fobj = open(self.fname) | |
|
270 | self.fobj = openpy.open(self.fname) | |
|
268 | 271 | |
|
269 | 272 | def reload(self): |
|
270 | 273 | """Reload source from disk and initialize state.""" |
|
271 | 274 | self.fload() |
|
272 | 275 | |
|
273 |
self.src = self.fobj |
|
|
276 | self.src = "".join(openpy.strip_encoding_cookie(self.fobj)) | |
|
274 | 277 | src_b = [b.strip() for b in self.re_stop.split(self.src) if b] |
|
275 | 278 | self._silent = [bool(self.re_silent.findall(b)) for b in src_b] |
|
276 | 279 | self._auto = [bool(self.re_auto.findall(b)) for b in src_b] |
@@ -1,7 +1,8 b'' | |||
|
1 | 1 | """Various display related classes. |
|
2 | 2 | |
|
3 | Authors : MinRK | |
|
3 | Authors : MinRK, dannystaple | |
|
4 | 4 | """ |
|
5 | import urllib | |
|
5 | 6 | |
|
6 | 7 | class YouTubeVideo(object): |
|
7 | 8 | """Class for embedding a YouTube Video in an IPython session, based on its video id. |
@@ -14,22 +15,37 b' class YouTubeVideo(object):' | |||
|
14 | 15 | |
|
15 | 16 | vid = YouTubeVideo("foo") |
|
16 | 17 | display(vid) |
|
18 | ||
|
19 | To start from 30 seconds: | |
|
20 | ||
|
21 | vid = YouTubeVideo("abc", start=30) | |
|
22 | display(vid) | |
|
23 | ||
|
24 | To calculate seconds from time as hours, minutes, seconds use: | |
|
25 | start=int(timedelta(hours=1, minutes=46, seconds=40).total_seconds()) | |
|
26 | ||
|
27 | Other parameters can be provided as documented at | |
|
28 | https://developers.google.com/youtube/player_parameters#parameter-subheader | |
|
17 | 29 | """ |
|
18 | 30 | |
|
19 | def __init__(self, id, width=400, height=300): | |
|
31 | def __init__(self, id, width=400, height=300, **kwargs): | |
|
20 | 32 | self.id = id |
|
21 | 33 | self.width = width |
|
22 | 34 | self.height = height |
|
35 | self.params = kwargs | |
|
23 | 36 | |
|
24 | 37 | def _repr_html_(self): |
|
25 | 38 | """return YouTube embed iframe for this video id""" |
|
39 | if self.params: | |
|
40 | params = "?" + urllib.urlencode(self.params) | |
|
41 | else: | |
|
42 | params = "" | |
|
26 | 43 | return """ |
|
27 | 44 | <iframe |
|
28 | 45 | width="%i" |
|
29 | 46 | height="%i" |
|
30 | src="http://www.youtube.com/embed/%s" | |
|
47 | src="http://www.youtube.com/embed/%s%s" | |
|
31 | 48 | frameborder="0" |
|
32 | 49 | allowfullscreen |
|
33 | 50 | ></iframe> |
|
34 | """%(self.width, self.height, self.id) | |
|
35 | ||
|
51 | """ % (self.width, self.height, self.id, params) |
@@ -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() |
@@ -169,8 +169,13 b' class BaseParallelApplication(BaseIPythonApplication):' | |||
|
169 | 169 | log_dir = self.profile_dir.log_dir |
|
170 | 170 | if self.clean_logs: |
|
171 | 171 | for f in os.listdir(log_dir): |
|
172 | if re.match(r'%s-\d+\.(log|err|out)'%self.name,f): | |
|
173 | os.remove(os.path.join(log_dir, f)) | |
|
172 | if re.match(r'%s-\d+\.(log|err|out)' % self.name, f): | |
|
173 | try: | |
|
174 | os.remove(os.path.join(log_dir, f)) | |
|
175 | except (OSError, IOError): | |
|
176 | # probably just conflict from sibling process | |
|
177 | # already removing it | |
|
178 | pass | |
|
174 | 179 | if self.log_to_file: |
|
175 | 180 | # Start logging to the new log file |
|
176 | 181 | log_filename = self.name + u'-' + str(os.getpid()) + u'.log' |
@@ -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: |
@@ -418,7 +418,7 b' class Client(HasTraits):' | |||
|
418 | 418 | location = cfg.setdefault('location', None) |
|
419 | 419 | |
|
420 | 420 | proto,addr = cfg['interface'].split('://') |
|
421 | addr = util.disambiguate_ip_address(addr) | |
|
421 | addr = util.disambiguate_ip_address(addr, location) | |
|
422 | 422 | cfg['interface'] = "%s://%s" % (proto, addr) |
|
423 | 423 | |
|
424 | 424 | # turn interface,port into full urls: |
@@ -705,12 +705,9 b' class Client(HasTraits):' | |||
|
705 | 705 | except: |
|
706 | 706 | content = error.wrap_exception() |
|
707 | 707 | # build a fake message: |
|
708 | parent = {} | |
|
709 | header = {} | |
|
710 | parent['msg_id'] = msg_id | |
|
711 | header['engine'] = uuid | |
|
712 | header['date'] = datetime.now() | |
|
713 | msg = dict(parent_header=parent, header=header, content=content) | |
|
708 | msg = self.session.msg('apply_reply', content=content) | |
|
709 | msg['parent_header']['msg_id'] = msg_id | |
|
710 | msg['metadata']['engine'] = uuid | |
|
714 | 711 | self._handle_apply_reply(msg) |
|
715 | 712 | |
|
716 | 713 | def _handle_execute_reply(self, msg): |
@@ -19,7 +19,7 b' import time' | |||
|
19 | 19 | import uuid |
|
20 | 20 | |
|
21 | 21 | import zmq |
|
22 | from zmq.devices import ThreadDevice | |
|
22 | from zmq.devices import ThreadDevice, ThreadMonitoredQueue | |
|
23 | 23 | from zmq.eventloop import ioloop, zmqstream |
|
24 | 24 | |
|
25 | 25 | from IPython.config.configurable import LoggingConfigurable |
@@ -39,8 +39,11 b' class Heart(object):' | |||
|
39 | 39 | You can specify the DEALER's IDENTITY via the optional heart_id argument.""" |
|
40 | 40 | device=None |
|
41 | 41 | id=None |
|
42 | def __init__(self, in_addr, out_addr, in_type=zmq.SUB, out_type=zmq.DEALER, heart_id=None): | |
|
43 | self.device = ThreadDevice(zmq.FORWARDER, in_type, out_type) | |
|
42 | def __init__(self, in_addr, out_addr, mon_addr=None, in_type=zmq.SUB, out_type=zmq.DEALER, mon_type=zmq.PUB, heart_id=None): | |
|
43 | if mon_addr is None: | |
|
44 | self.device = ThreadDevice(zmq.FORWARDER, in_type, out_type) | |
|
45 | else: | |
|
46 | self.device = ThreadMonitoredQueue(in_type, out_type, mon_type, in_prefix=b"", out_prefix=b"") | |
|
44 | 47 | # do not allow the device to share global Context.instance, |
|
45 | 48 | # which is the default behavior in pyzmq > 2.1.10 |
|
46 | 49 | self.device.context_factory = zmq.Context |
@@ -48,6 +51,8 b' class Heart(object):' | |||
|
48 | 51 | self.device.daemon=True |
|
49 | 52 | self.device.connect_in(in_addr) |
|
50 | 53 | self.device.connect_out(out_addr) |
|
54 | if mon_addr is not None: | |
|
55 | self.device.connect_mon(mon_addr) | |
|
51 | 56 | if in_type == zmq.SUB: |
|
52 | 57 | self.device.setsockopt_in(zmq.SUBSCRIBE, b"") |
|
53 | 58 | if heart_id is None: |
@@ -122,7 +127,7 b' class HeartMonitor(LoggingConfigurable):' | |||
|
122 | 127 | map(self.handle_heart_failure, heartfailures) |
|
123 | 128 | self.on_probation = missed_beats.intersection(self.hearts) |
|
124 | 129 | self.responses = set() |
|
125 |
# |
|
|
130 | #print self.on_probation, self.hearts | |
|
126 | 131 | # self.log.debug("heartbeat::beat %.3f, %i beating hearts", self.lifetime, len(self.hearts)) |
|
127 | 132 | self.pingstream.send(str_to_bytes(str(self.lifetime))) |
|
128 | 133 | # flush stream to force immediate socket send |
@@ -165,18 +170,3 b' class HeartMonitor(LoggingConfigurable):' | |||
|
165 | 170 | else: |
|
166 | 171 | self.log.warn("heartbeat::got bad heartbeat (possibly old?): %s (current=%.3f)", msg[1], self.lifetime) |
|
167 | 172 | |
|
168 | ||
|
169 | if __name__ == '__main__': | |
|
170 | loop = ioloop.IOLoop.instance() | |
|
171 | context = zmq.Context() | |
|
172 | pub = context.socket(zmq.PUB) | |
|
173 | pub.bind('tcp://127.0.0.1:5555') | |
|
174 | router = context.socket(zmq.ROUTER) | |
|
175 | router.bind('tcp://127.0.0.1:5556') | |
|
176 | ||
|
177 | outstream = zmqstream.ZMQStream(pub, loop) | |
|
178 | instream = zmqstream.ZMQStream(router, loop) | |
|
179 | ||
|
180 | hb = HeartMonitor(loop, outstream, instream) | |
|
181 | ||
|
182 | loop.start() |
@@ -904,7 +904,7 b' class Hub(SessionFactory):' | |||
|
904 | 904 | |
|
905 | 905 | self.log.debug("registration::register_engine(%i, %r)", eid, uuid) |
|
906 | 906 | |
|
907 | content = dict(id=eid,status='ok') | |
|
907 | content = dict(id=eid,status='ok',hb_period=self.heartmonitor.period) | |
|
908 | 908 | # check if requesting available IDs: |
|
909 | 909 | if cast_bytes(uuid) in self.by_ident: |
|
910 | 910 | try: |
@@ -25,7 +25,7 b' from zmq.eventloop import ioloop, zmqstream' | |||
|
25 | 25 | from IPython.external.ssh import tunnel |
|
26 | 26 | # internal |
|
27 | 27 | from IPython.utils.traitlets import ( |
|
28 |
Instance, Dict, Integer, Type, |
|
|
28 | Instance, Dict, Integer, Type, Float, Integer, Unicode, CBytes, Bool | |
|
29 | 29 | ) |
|
30 | 30 | from IPython.utils.py3compat import cast_bytes |
|
31 | 31 | |
@@ -50,9 +50,14 b' class EngineFactory(RegistrationFactory):' | |||
|
50 | 50 | help="""The location (an IP address) of the controller. This is |
|
51 | 51 | used for disambiguating URLs, to determine whether |
|
52 | 52 | loopback should be used to connect or the public address.""") |
|
53 |
timeout= |
|
|
53 | timeout=Float(5.0, config=True, | |
|
54 | 54 | help="""The time (in seconds) to wait for the Controller to respond |
|
55 | 55 | to registration requests before giving up.""") |
|
56 | max_heartbeat_misses=Integer(50, config=True, | |
|
57 | help="""The maximum number of times a check for the heartbeat ping of a | |
|
58 | controller can be missed before shutting down the engine. | |
|
59 | ||
|
60 | If set to 0, the check is disabled.""") | |
|
56 | 61 | sshserver=Unicode(config=True, |
|
57 | 62 | help="""The SSH server to use for tunneling connections to the Controller.""") |
|
58 | 63 | sshkey=Unicode(config=True, |
@@ -60,12 +65,23 b' class EngineFactory(RegistrationFactory):' | |||
|
60 | 65 | paramiko=Bool(sys.platform == 'win32', config=True, |
|
61 | 66 | help="""Whether to use paramiko instead of openssh for tunnels.""") |
|
62 | 67 | |
|
68 | ||
|
63 | 69 | # not configurable: |
|
64 | 70 | connection_info = Dict() |
|
65 | 71 | user_ns = Dict() |
|
66 | 72 | id = Integer(allow_none=True) |
|
67 | 73 | registrar = Instance('zmq.eventloop.zmqstream.ZMQStream') |
|
68 | 74 | kernel = Instance(Kernel) |
|
75 | hb_check_period=Integer() | |
|
76 | ||
|
77 | # States for the heartbeat monitoring | |
|
78 | # Initial values for monitored and pinged must satisfy "monitored > pinged == False" so that | |
|
79 | # during the first check no "missed" ping is reported. Must be floats for Python 3 compatibility. | |
|
80 | _hb_last_pinged = 0.0 | |
|
81 | _hb_last_monitored = 0.0 | |
|
82 | _hb_missed_beats = 0 | |
|
83 | # The zmq Stream which receives the pings from the Heart | |
|
84 | _hb_listener = None | |
|
69 | 85 | |
|
70 | 86 | bident = CBytes() |
|
71 | 87 | ident = Unicode() |
@@ -134,6 +150,11 b' class EngineFactory(RegistrationFactory):' | |||
|
134 | 150 | # print (self.session.key) |
|
135 | 151 | self.session.send(self.registrar, "registration_request", content=content) |
|
136 | 152 | |
|
153 | def _report_ping(self, msg): | |
|
154 | """Callback for when the heartmonitor.Heart receives a ping""" | |
|
155 | #self.log.debug("Received a ping: %s", msg) | |
|
156 | self._hb_last_pinged = time.time() | |
|
157 | ||
|
137 | 158 | def complete_registration(self, msg, connect, maybe_tunnel): |
|
138 | 159 | # print msg |
|
139 | 160 | self._abort_dc.stop() |
@@ -156,8 +177,20 b' class EngineFactory(RegistrationFactory):' | |||
|
156 | 177 | # possibly forward hb ports with tunnels |
|
157 | 178 | hb_ping = maybe_tunnel(url('hb_ping')) |
|
158 | 179 | hb_pong = maybe_tunnel(url('hb_pong')) |
|
180 | ||
|
181 | hb_monitor = None | |
|
182 | if self.max_heartbeat_misses > 0: | |
|
183 | # Add a monitor socket which will record the last time a ping was seen | |
|
184 | mon = self.context.socket(zmq.SUB) | |
|
185 | mport = mon.bind_to_random_port('tcp://127.0.0.1') | |
|
186 | mon.setsockopt(zmq.SUBSCRIBE, b"") | |
|
187 | self._hb_listener = zmqstream.ZMQStream(mon, self.loop) | |
|
188 | self._hb_listener.on_recv(self._report_ping) | |
|
189 | ||
|
190 | ||
|
191 | hb_monitor = "tcp://127.0.0.1:%i"%mport | |
|
159 | 192 | |
|
160 | heart = Heart(hb_ping, hb_pong, heart_id=identity) | |
|
193 | heart = Heart(hb_ping, hb_pong, hb_monitor , heart_id=identity) | |
|
161 | 194 | heart.start() |
|
162 | 195 | |
|
163 | 196 | # create Shell Connections (MUX, Task, etc.): |
@@ -201,6 +234,20 b' class EngineFactory(RegistrationFactory):' | |||
|
201 | 234 | |
|
202 | 235 | self.kernel.shell.display_pub.topic = cast_bytes('engine.%i.displaypub' % self.id) |
|
203 | 236 | |
|
237 | ||
|
238 | # periodically check the heartbeat pings of the controller | |
|
239 | # Should be started here and not in "start()" so that the right period can be taken | |
|
240 | # from the hubs HeartBeatMonitor.period | |
|
241 | if self.max_heartbeat_misses > 0: | |
|
242 | # Use a slightly bigger check period than the hub signal period to not warn unnecessary | |
|
243 | self.hb_check_period = int(content['hb_period'])+10 | |
|
244 | self.log.info("Starting to monitor the heartbeat signal from the hub every %i ms." , self.hb_check_period) | |
|
245 | self._hb_reporter = ioloop.PeriodicCallback(self._hb_monitor, self.hb_check_period, self.loop) | |
|
246 | self._hb_reporter.start() | |
|
247 | else: | |
|
248 | self.log.info("Monitoring of the heartbeat signal from the hub is not enabled.") | |
|
249 | ||
|
250 | ||
|
204 | 251 | # FIXME: This is a hack until IPKernelApp and IPEngineApp can be fully merged |
|
205 | 252 | app = IPKernelApp(config=self.config, shell=self.kernel.shell, kernel=self.kernel, log=self.log) |
|
206 | 253 | app.init_profile_dir() |
@@ -228,9 +275,29 b' class EngineFactory(RegistrationFactory):' | |||
|
228 | 275 | time.sleep(1) |
|
229 | 276 | sys.exit(255) |
|
230 | 277 | |
|
278 | def _hb_monitor(self): | |
|
279 | """Callback to monitor the heartbeat from the controller""" | |
|
280 | self._hb_listener.flush() | |
|
281 | if self._hb_last_monitored > self._hb_last_pinged: | |
|
282 | self._hb_missed_beats += 1 | |
|
283 | self.log.warn("No heartbeat in the last %s ms (%s time(s) in a row).", self.hb_check_period, self._hb_missed_beats) | |
|
284 | else: | |
|
285 | #self.log.debug("Heartbeat received (after missing %s beats).", self._hb_missed_beats) | |
|
286 | self._hb_missed_beats = 0 | |
|
287 | ||
|
288 | if self._hb_missed_beats >= self.max_heartbeat_misses: | |
|
289 | self.log.fatal("Maximum number of heartbeats misses reached (%s times %s ms), shutting down.", | |
|
290 | self.max_heartbeat_misses, self.hb_check_period) | |
|
291 | self.session.send(self.registrar, "unregistration_request", content=dict(id=self.id)) | |
|
292 | self.loop.stop() | |
|
293 | ||
|
294 | self._hb_last_monitored = time.time() | |
|
295 | ||
|
296 | ||
|
231 | 297 | def start(self): |
|
232 | 298 | dc = ioloop.DelayedCallback(self.register, 0, self.loop) |
|
233 | 299 | dc.start() |
|
234 | 300 | self._abort_dc = ioloop.DelayedCallback(self.abort, self.timeout*1000, self.loop) |
|
235 | 301 | self._abort_dc.start() |
|
236 | 302 | |
|
303 |
@@ -21,6 +21,7 b' import time' | |||
|
21 | 21 | |
|
22 | 22 | import zmq |
|
23 | 23 | from nose import SkipTest |
|
24 | from nose.plugins.attrib import attr | |
|
24 | 25 | |
|
25 | 26 | from IPython import parallel as pmod |
|
26 | 27 | from IPython.parallel import error |
@@ -38,9 +39,9 b' class TestLoadBalancedView(ClusterTestCase):' | |||
|
38 | 39 | ClusterTestCase.setUp(self) |
|
39 | 40 | self.view = self.client.load_balanced_view() |
|
40 | 41 | |
|
42 | @attr('crash') | |
|
41 | 43 | def test_z_crash_task(self): |
|
42 | 44 | """test graceful handling of engine death (balanced)""" |
|
43 | raise SkipTest("crash tests disabled, due to undesirable crash reports") | |
|
44 | 45 | # self.add_engines(1) |
|
45 | 46 | ar = self.view.apply_async(crash) |
|
46 | 47 | self.assertRaisesRemote(error.EngineError, ar.get, 10) |
@@ -24,6 +24,7 b' from StringIO import StringIO' | |||
|
24 | 24 | |
|
25 | 25 | import zmq |
|
26 | 26 | from nose import SkipTest |
|
27 | from nose.plugins.attrib import attr | |
|
27 | 28 | |
|
28 | 29 | from IPython.testing import decorators as dec |
|
29 | 30 | from IPython.testing.ipunittest import ParametricTestCase |
@@ -51,9 +52,9 b' class TestView(ClusterTestCase, ParametricTestCase):' | |||
|
51 | 52 | time.sleep(2) |
|
52 | 53 | super(TestView, self).setUp() |
|
53 | 54 | |
|
55 | @attr('crash') | |
|
54 | 56 | def test_z_crash_mux(self): |
|
55 | 57 | """test graceful handling of engine death (direct)""" |
|
56 | raise SkipTest("crash tests disabled, due to undesirable crash reports") | |
|
57 | 58 | # self.add_engines(1) |
|
58 | 59 | eid = self.client.ids[-1] |
|
59 | 60 | ar = self.client[eid].apply_async(crash) |
@@ -700,4 +701,22 b' class TestView(ClusterTestCase, ParametricTestCase):' | |||
|
700 | 701 | drank = amr.get(5) |
|
701 | 702 | self.assertEqual(drank, [ r*2 for r in ranks ]) |
|
702 | 703 | |
|
704 | def test_nested_getitem_setitem(self): | |
|
705 | """get and set with view['a.b']""" | |
|
706 | view = self.client[-1] | |
|
707 | view.execute('\n'.join([ | |
|
708 | 'class A(object): pass', | |
|
709 | 'a = A()', | |
|
710 | 'a.b = 128', | |
|
711 | ]), block=True) | |
|
712 | ra = pmod.Reference('a') | |
|
713 | ||
|
714 | r = view.apply_sync(lambda x: x.b, ra) | |
|
715 | self.assertEqual(r, 128) | |
|
716 | self.assertEqual(view['a.b'], 128) | |
|
717 | ||
|
718 | view['a.b'] = 0 | |
|
703 | 719 | |
|
720 | r = view.apply_sync(lambda x: x.b, ra) | |
|
721 | self.assertEqual(r, 0) | |
|
722 | self.assertEqual(view['a.b'], 0) |
@@ -229,21 +229,24 b' def interactive(f):' | |||
|
229 | 229 | @interactive |
|
230 | 230 | def _push(**ns): |
|
231 | 231 | """helper method for implementing `client.push` via `client.apply`""" |
|
232 | globals().update(ns) | |
|
232 | user_ns = globals() | |
|
233 | tmp = '_IP_PUSH_TMP_' | |
|
234 | while tmp in user_ns: | |
|
235 | tmp = tmp + '_' | |
|
236 | try: | |
|
237 | for name, value in ns.iteritems(): | |
|
238 | user_ns[tmp] = value | |
|
239 | exec "%s = %s" % (name, tmp) in user_ns | |
|
240 | finally: | |
|
241 | user_ns.pop(tmp, None) | |
|
233 | 242 | |
|
234 | 243 | @interactive |
|
235 | 244 | def _pull(keys): |
|
236 | 245 | """helper method for implementing `client.pull` via `client.apply`""" |
|
237 | user_ns = globals() | |
|
238 | 246 | if isinstance(keys, (list,tuple, set)): |
|
239 | for key in keys: | |
|
240 | if key not in user_ns: | |
|
241 | raise NameError("name '%s' is not defined"%key) | |
|
242 | return map(user_ns.get, keys) | |
|
247 | return map(lambda key: eval(key, globals()), keys) | |
|
243 | 248 | else: |
|
244 | if keys not in user_ns: | |
|
245 | raise NameError("name '%s' is not defined"%keys) | |
|
246 | return user_ns.get(keys) | |
|
249 | return eval(keys, globals()) | |
|
247 | 250 | |
|
248 | 251 | @interactive |
|
249 | 252 | def _execute(code): |
@@ -396,6 +396,8 b' class IPTester(object):' | |||
|
396 | 396 | """Run the stored commands""" |
|
397 | 397 | try: |
|
398 | 398 | retcode = self._run_cmd() |
|
399 | except KeyboardInterrupt: | |
|
400 | return -signal.SIGINT | |
|
399 | 401 | except: |
|
400 | 402 | import traceback |
|
401 | 403 | traceback.print_exc() |
@@ -412,17 +414,22 b' class IPTester(object):' | |||
|
412 | 414 | continue # process is already dead |
|
413 | 415 | |
|
414 | 416 | try: |
|
415 | print('Cleaning stale PID: %d' % subp.pid) | |
|
417 | print('Cleaning up stale PID: %d' % subp.pid) | |
|
416 | 418 | subp.kill() |
|
417 | 419 | except: # (OSError, WindowsError) ? |
|
418 | 420 | # This is just a best effort, if we fail or the process was |
|
419 | 421 | # really gone, ignore it. |
|
420 | 422 | pass |
|
423 | else: | |
|
424 | for i in range(10): | |
|
425 | if subp.poll() is None: | |
|
426 | time.sleep(0.1) | |
|
427 | else: | |
|
428 | break | |
|
421 | 429 | |
|
422 | 430 | if subp.poll() is None: |
|
423 | 431 | # The process did not die... |
|
424 | print('... failed. Manual cleanup may be required.' | |
|
425 | % subp.pid) | |
|
432 | print('... failed. Manual cleanup may be required.') | |
|
426 | 433 | |
|
427 | 434 | def make_runners(inc_slow=False): |
|
428 | 435 | """Define the top-level packages that need to be tested. |
@@ -476,6 +483,8 b' def run_iptest():' | |||
|
476 | 483 | # setuptools devs refuse to fix this problem! |
|
477 | 484 | '--exe', |
|
478 | 485 | ] |
|
486 | if '-a' not in argv and '-A' not in argv: | |
|
487 | argv = argv + ['-a', '!crash'] | |
|
479 | 488 | |
|
480 | 489 | if nose.__version__ >= '0.11': |
|
481 | 490 | # I don't fully understand why we need this one, but depending on what |
@@ -533,6 +542,9 b' def run_iptestall(inc_slow=False):' | |||
|
533 | 542 | res = runner.run() |
|
534 | 543 | if res: |
|
535 | 544 | failed.append( (name, runner) ) |
|
545 | if res == -signal.SIGINT: | |
|
546 | print("Interrupted") | |
|
547 | break | |
|
536 | 548 | finally: |
|
537 | 549 | os.chdir(curdir) |
|
538 | 550 | t_end = time.time() |
@@ -30,6 +30,8 b' formatting (which is the hard part).' | |||
|
30 | 30 | """ |
|
31 | 31 | from __future__ import print_function |
|
32 | 32 | |
|
33 | from __future__ import unicode_literals | |
|
34 | ||
|
33 | 35 | __all__ = ['ANSICodeColors','Parser'] |
|
34 | 36 | |
|
35 | 37 | _scheme_default = 'Linux' |
@@ -7,7 +7,7 b' Much of the code is taken from the tokenize module in Python 3.2.' | |||
|
7 | 7 | from __future__ import absolute_import |
|
8 | 8 | |
|
9 | 9 | import io |
|
10 | from io import TextIOWrapper | |
|
10 | from io import TextIOWrapper, BytesIO | |
|
11 | 11 | import re |
|
12 | 12 | import urllib |
|
13 | 13 | |
@@ -120,6 +120,32 b' except ImportError:' | |||
|
120 | 120 | text.mode = 'r' |
|
121 | 121 | return text |
|
122 | 122 | |
|
123 | def source_to_unicode(txt, errors='replace', skip_encoding_cookie=True): | |
|
124 | """Converts a bytes string with python source code to unicode. | |
|
125 | ||
|
126 | Unicode strings are passed through unchanged. Byte strings are checked | |
|
127 | for the python source file encoding cookie to determine encoding. | |
|
128 | txt can be either a bytes buffer or a string containing the source | |
|
129 | code. | |
|
130 | """ | |
|
131 | if isinstance(txt, unicode): | |
|
132 | return txt | |
|
133 | if isinstance(txt, bytes): | |
|
134 | buffer = BytesIO(txt) | |
|
135 | else: | |
|
136 | buffer = txt | |
|
137 | try: | |
|
138 | encoding, _ = detect_encoding(buffer.readline) | |
|
139 | except SyntaxError: | |
|
140 | encoding = "ascii" | |
|
141 | buffer.seek(0) | |
|
142 | text = TextIOWrapper(buffer, encoding, errors=errors, line_buffering=True) | |
|
143 | text.mode = 'r' | |
|
144 | if skip_encoding_cookie: | |
|
145 | return u"".join(strip_encoding_cookie(text)) | |
|
146 | else: | |
|
147 | return text.read() | |
|
148 | ||
|
123 | 149 | def strip_encoding_cookie(filelike): |
|
124 | 150 | """Generator to pull lines from a text-mode file, skipping the encoding |
|
125 | 151 | cookie if it is found in the first two lines. |
@@ -181,12 +207,13 b" def read_py_url(url, errors='replace', skip_encoding_cookie=True):" | |||
|
181 | 207 | """ |
|
182 | 208 | response = urllib.urlopen(url) |
|
183 | 209 | buffer = io.BytesIO(response.read()) |
|
184 | encoding, lines = detect_encoding(buffer.readline) | |
|
185 | buffer.seek(0) | |
|
186 | text = TextIOWrapper(buffer, encoding, errors=errors, line_buffering=True) | |
|
187 | text.mode = 'r' | |
|
188 | if skip_encoding_cookie: | |
|
189 | return "".join(strip_encoding_cookie(text)) | |
|
190 | else: | |
|
191 | return text.read() | |
|
210 | return source_to_unicode(buffer, errors, skip_encoding_cookie) | |
|
192 | 211 | |
|
212 | def _list_readline(x): | |
|
213 | """Given a list, returns a readline() function that returns the next element | |
|
214 | with each call. | |
|
215 | """ | |
|
216 | x = iter(x) | |
|
217 | def readline(): | |
|
218 | return next(x) | |
|
219 | return readline |
@@ -19,6 +19,7 b' import sys' | |||
|
19 | 19 | import tempfile |
|
20 | 20 | import warnings |
|
21 | 21 | from hashlib import md5 |
|
22 | import glob | |
|
22 | 23 | |
|
23 | 24 | import IPython |
|
24 | 25 | from IPython.testing.skipdoctest import skip_doctest |
@@ -355,6 +356,31 b' def expand_path(s):' | |||
|
355 | 356 | return s |
|
356 | 357 | |
|
357 | 358 | |
|
359 | def unescape_glob(string): | |
|
360 | """Unescape glob pattern in `string`.""" | |
|
361 | def unescape(s): | |
|
362 | for pattern in '*[]!?': | |
|
363 | s = s.replace(r'\{0}'.format(pattern), pattern) | |
|
364 | return s | |
|
365 | return '\\'.join(map(unescape, string.split('\\\\'))) | |
|
366 | ||
|
367 | ||
|
368 | def shellglob(args): | |
|
369 | """ | |
|
370 | Do glob expansion for each element in `args` and return a flattened list. | |
|
371 | ||
|
372 | Unmatched glob pattern will remain as-is in the returned list. | |
|
373 | ||
|
374 | """ | |
|
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 | |
|
379 | for a in args: | |
|
380 | expanded.extend(glob.glob(a) or [unescape(a)]) | |
|
381 | return expanded | |
|
382 | ||
|
383 | ||
|
358 | 384 | def target_outdated(target,deps): |
|
359 | 385 | """Determine whether a target is out of date. |
|
360 | 386 |
@@ -98,12 +98,8 b' class CannedFunction(CannedObject):' | |||
|
98 | 98 | def get_object(self, g=None): |
|
99 | 99 | # try to load function back into its module: |
|
100 | 100 | if not self.module.startswith('__'): |
|
101 | try: | |
|
102 |
|
|
|
103 | except ImportError: | |
|
104 | pass | |
|
105 | else: | |
|
106 | g = sys.modules[self.module].__dict__ | |
|
101 | __import__(self.module) | |
|
102 | g = sys.modules[self.module].__dict__ | |
|
107 | 103 | |
|
108 | 104 | if g is None: |
|
109 | 105 | g = {} |
@@ -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: |
@@ -3,14 +3,14 b'' | |||
|
3 | 3 | This is copied from the stdlib and will be standard in Python 3.2 and onwards. |
|
4 | 4 | """ |
|
5 | 5 | |
|
6 | import os as _os | |
|
7 | ||
|
6 | 8 | # This code should only be used in Python versions < 3.2, since after that we |
|
7 | 9 | # can rely on the stdlib itself. |
|
8 | 10 | try: |
|
9 | 11 | from tempfile import TemporaryDirectory |
|
10 | 12 | |
|
11 | 13 | except ImportError: |
|
12 | ||
|
13 | import os as _os | |
|
14 | 14 | from tempfile import mkdtemp, template |
|
15 | 15 | |
|
16 | 16 | class TemporaryDirectory(object): |
@@ -74,3 +74,33 b' except ImportError:' | |||
|
74 | 74 | self._rmdir(path) |
|
75 | 75 | except self._os_error: |
|
76 | 76 | pass |
|
77 | ||
|
78 | ||
|
79 | class NamedFileInTemporaryDirectory(object): | |
|
80 | ||
|
81 | def __init__(self, filename, mode='w+b', bufsize=-1, **kwds): | |
|
82 | """ | |
|
83 | Open a file named `filename` in a temporary directory. | |
|
84 | ||
|
85 | This context manager is preferred over `NamedTemporaryFile` in | |
|
86 | stdlib `tempfile` when one needs to reopen the file. | |
|
87 | ||
|
88 | Arguments `mode` and `bufsize` are passed to `open`. | |
|
89 | Rest of the arguments are passed to `TemporaryDirectory`. | |
|
90 | ||
|
91 | """ | |
|
92 | self._tmpdir = TemporaryDirectory(**kwds) | |
|
93 | path = _os.path.join(self._tmpdir.name, filename) | |
|
94 | self.file = open(path, mode, bufsize) | |
|
95 | ||
|
96 | def cleanup(self): | |
|
97 | self.file.close() | |
|
98 | self._tmpdir.cleanup() | |
|
99 | ||
|
100 | __del__ = cleanup | |
|
101 | ||
|
102 | def __enter__(self): | |
|
103 | return self.file | |
|
104 | ||
|
105 | def __exit__(self, type, value, traceback): | |
|
106 | self.cleanup() |
@@ -81,7 +81,7 b' def _set_term_title_xterm(title):' | |||
|
81 | 81 | |
|
82 | 82 | if os.name == 'posix': |
|
83 | 83 | TERM = os.environ.get('TERM','') |
|
84 | if (TERM == 'xterm') or (TERM == 'xterm-color'): | |
|
84 | if TERM.startswith('xterm'): | |
|
85 | 85 | _set_term_title = _set_term_title_xterm |
|
86 | 86 | |
|
87 | 87 |
@@ -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 | |
@@ -32,6 +33,7 b' from IPython.testing.decorators import skip_if_not_win32, skip_win32' | |||
|
32 | 33 | from IPython.testing.tools import make_tempfile, AssertPrints |
|
33 | 34 | from IPython.utils import path, io |
|
34 | 35 | from IPython.utils import py3compat |
|
36 | from IPython.utils.tempdir import TemporaryDirectory | |
|
35 | 37 | |
|
36 | 38 | # Platform-dependent imports |
|
37 | 39 | try: |
@@ -444,3 +446,79 b' def test_unicode_in_filename():' | |||
|
444 | 446 | path.get_py_filename(u'fooéè.py', force_win32=False) |
|
445 | 447 | except IOError as ex: |
|
446 | 448 | str(ex) |
|
449 | ||
|
450 | ||
|
451 | class TestShellGlob(object): | |
|
452 | ||
|
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 | ||
|
461 | with cls.in_tempdir(): | |
|
462 | # Create empty files | |
|
463 | for fname in cls.filenames: | |
|
464 | open(os.path.join(td, fname), 'w').close() | |
|
465 | ||
|
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 | |
|
477 | finally: | |
|
478 | os.chdir(save) | |
|
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 | ||
|
518 | ||
|
519 | def test_unescape_glob(): | |
|
520 | nt.assert_equals(path.unescape_glob(r'\*\[\!\]\?'), '*[!]?') | |
|
521 | nt.assert_equals(path.unescape_glob(r'\\*'), r'\*') | |
|
522 | nt.assert_equals(path.unescape_glob(r'\\\*'), r'\*') | |
|
523 | nt.assert_equals(path.unescape_glob(r'\\a'), r'\a') | |
|
524 | nt.assert_equals(path.unescape_glob(r'\a'), r'\a') |
@@ -25,10 +25,11 b' import re' | |||
|
25 | 25 | import sys |
|
26 | 26 | from unittest import TestCase |
|
27 | 27 | |
|
28 | import nose.tools as nt | |
|
28 | 29 | from nose import SkipTest |
|
29 | 30 | |
|
30 | 31 | from IPython.utils.traitlets import ( |
|
31 | HasTraits, MetaHasTraits, TraitType, Any, CBytes, | |
|
32 | HasTraits, MetaHasTraits, TraitType, Any, CBytes, Dict, | |
|
32 | 33 | Int, Long, Integer, Float, Complex, Bytes, Unicode, TraitError, |
|
33 | 34 | Undefined, Type, This, Instance, TCPAddress, List, Tuple, |
|
34 | 35 | ObjectName, DottedObjectName, CRegExp |
@@ -906,3 +907,14 b' class TestCRegExp(TraitTestBase):' | |||
|
906 | 907 | _default_value = re.compile(r'') |
|
907 | 908 | _good_values = [r'\d+', re.compile(r'\d+')] |
|
908 | 909 | _bad_values = [r'(', None, ()] |
|
910 | ||
|
911 | class DictTrait(HasTraits): | |
|
912 | value = Dict() | |
|
913 | ||
|
914 | def test_dict_assignment(): | |
|
915 | d = dict() | |
|
916 | c = DictTrait() | |
|
917 | c.value = d | |
|
918 | d['a'] = 5 | |
|
919 | nt.assert_equal(d, c.value) | |
|
920 | nt.assert_true(c.value is d) |
@@ -117,6 +117,13 b' def repr_type(obj):' | |||
|
117 | 117 | return msg |
|
118 | 118 | |
|
119 | 119 | |
|
120 | def is_trait(t): | |
|
121 | """ Returns whether the given value is an instance or subclass of TraitType. | |
|
122 | """ | |
|
123 | return (isinstance(t, TraitType) or | |
|
124 | (isinstance(t, type) and issubclass(t, TraitType))) | |
|
125 | ||
|
126 | ||
|
120 | 127 | def parse_notifier_name(name): |
|
121 | 128 | """Convert the name argument to a list of names. |
|
122 | 129 | |
@@ -302,8 +309,8 b' class TraitType(object):' | |||
|
302 | 309 | def __set__(self, obj, value): |
|
303 | 310 | new_value = self._validate(obj, value) |
|
304 | 311 | old_value = self.__get__(obj) |
|
312 | obj._trait_values[self.name] = new_value | |
|
305 | 313 | if old_value != new_value: |
|
306 | obj._trait_values[self.name] = new_value | |
|
307 | 314 | obj._notify_trait(self.name, old_value, new_value) |
|
308 | 315 | |
|
309 | 316 | def _validate(self, obj, value): |
@@ -920,11 +927,15 b' else:' | |||
|
920 | 927 | def validate(self, obj, value): |
|
921 | 928 | if isinstance(value, int): |
|
922 | 929 | return value |
|
923 |
|
|
|
930 | if isinstance(value, long): | |
|
924 | 931 | # downcast longs that fit in int: |
|
925 | 932 | # note that int(n > sys.maxint) returns a long, so |
|
926 | 933 | # we don't need a condition on this cast |
|
927 | 934 | return int(value) |
|
935 | if sys.platform == "cli": | |
|
936 | from System import Int64 | |
|
937 | if isinstance(value, Int64): | |
|
938 | return int(value) | |
|
928 | 939 | self.error(obj, value) |
|
929 | 940 | |
|
930 | 941 | |
@@ -1165,10 +1176,8 b' class Container(Instance):' | |||
|
1165 | 1176 | further keys for extensions to the Trait (e.g. config) |
|
1166 | 1177 | |
|
1167 | 1178 | """ |
|
1168 | istrait = lambda t: isinstance(t, type) and issubclass(t, TraitType) | |
|
1169 | ||
|
1170 | 1179 | # allow List([values]): |
|
1171 | if default_value is None and not istrait(trait): | |
|
1180 | if default_value is None and not is_trait(trait): | |
|
1172 | 1181 | default_value = trait |
|
1173 | 1182 | trait = None |
|
1174 | 1183 | |
@@ -1179,8 +1188,8 b' class Container(Instance):' | |||
|
1179 | 1188 | else: |
|
1180 | 1189 | raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value)) |
|
1181 | 1190 | |
|
1182 | if istrait(trait): | |
|
1183 | self._trait = trait() | |
|
1191 | if is_trait(trait): | |
|
1192 | self._trait = trait() if isinstance(trait, type) else trait | |
|
1184 | 1193 | self._trait.name = 'element' |
|
1185 | 1194 | elif trait is not None: |
|
1186 | 1195 | raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait)) |
@@ -1220,7 +1229,7 b' class List(Container):' | |||
|
1220 | 1229 | """An instance of a Python list.""" |
|
1221 | 1230 | klass = list |
|
1222 | 1231 | |
|
1223 |
def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.max |
|
|
1232 | def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxsize, | |
|
1224 | 1233 | allow_none=True, **metadata): |
|
1225 | 1234 | """Create a List trait type from a list, set, or tuple. |
|
1226 | 1235 | |
@@ -1249,7 +1258,7 b' class List(Container):' | |||
|
1249 | 1258 | minlen : Int [ default 0 ] |
|
1250 | 1259 | The minimum length of the input list |
|
1251 | 1260 | |
|
1252 |
maxlen : Int [ default sys.max |
|
|
1261 | maxlen : Int [ default sys.maxsize ] | |
|
1253 | 1262 | The maximum length of the input list |
|
1254 | 1263 | |
|
1255 | 1264 | allow_none : Bool [ default True ] |
@@ -1327,10 +1336,8 b' class Tuple(Container):' | |||
|
1327 | 1336 | default_value = metadata.pop('default_value', None) |
|
1328 | 1337 | allow_none = metadata.pop('allow_none', True) |
|
1329 | 1338 | |
|
1330 | istrait = lambda t: isinstance(t, type) and issubclass(t, TraitType) | |
|
1331 | ||
|
1332 | 1339 | # allow Tuple((values,)): |
|
1333 | if len(traits) == 1 and default_value is None and not istrait(traits[0]): | |
|
1340 | if len(traits) == 1 and default_value is None and not is_trait(traits[0]): | |
|
1334 | 1341 | default_value = traits[0] |
|
1335 | 1342 | traits = () |
|
1336 | 1343 | |
@@ -1343,7 +1350,7 b' class Tuple(Container):' | |||
|
1343 | 1350 | |
|
1344 | 1351 | self._traits = [] |
|
1345 | 1352 | for trait in traits: |
|
1346 | t = trait() | |
|
1353 | t = trait() if isinstance(trait, type) else trait | |
|
1347 | 1354 | t.name = 'element' |
|
1348 | 1355 | self._traits.append(t) |
|
1349 | 1356 |
@@ -21,7 +21,7 b' from IPython.utils.py3compat import bytes_to_str' | |||
|
21 | 21 | from parentpoller import ParentPollerWindows |
|
22 | 22 | |
|
23 | 23 | def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0, |
|
24 | ip=LOCALHOST, key=b''): | |
|
24 | ip=LOCALHOST, key=b'', transport='tcp'): | |
|
25 | 25 | """Generates a JSON config file, including the selection of random ports. |
|
26 | 26 | |
|
27 | 27 | Parameters |
@@ -54,17 +54,26 b' def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, ' | |||
|
54 | 54 | fname = tempfile.mktemp('.json') |
|
55 | 55 | |
|
56 | 56 | # Find open ports as necessary. |
|
57 | ||
|
57 | 58 | ports = [] |
|
58 | 59 | ports_needed = int(shell_port <= 0) + int(iopub_port <= 0) + \ |
|
59 | 60 | int(stdin_port <= 0) + int(hb_port <= 0) |
|
60 | for i in xrange(ports_needed): | |
|
61 | sock = socket.socket() | |
|
62 | sock.bind(('', 0)) | |
|
63 | ports.append(sock) | |
|
64 | for i, sock in enumerate(ports): | |
|
65 | port = sock.getsockname()[1] | |
|
66 | sock.close() | |
|
67 | ports[i] = port | |
|
61 | if transport == 'tcp': | |
|
62 | for i in range(ports_needed): | |
|
63 | sock = socket.socket() | |
|
64 | sock.bind(('', 0)) | |
|
65 | ports.append(sock) | |
|
66 | for i, sock in enumerate(ports): | |
|
67 | port = sock.getsockname()[1] | |
|
68 | sock.close() | |
|
69 | ports[i] = port | |
|
70 | else: | |
|
71 | N = 1 | |
|
72 | for i in range(ports_needed): | |
|
73 | while os.path.exists("%s-%s" % (ip, str(N))): | |
|
74 | N += 1 | |
|
75 | ports.append(N) | |
|
76 | N += 1 | |
|
68 | 77 | if shell_port <= 0: |
|
69 | 78 | shell_port = ports.pop(0) |
|
70 | 79 | if iopub_port <= 0: |
@@ -81,6 +90,7 b' def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, ' | |||
|
81 | 90 | ) |
|
82 | 91 | cfg['ip'] = ip |
|
83 | 92 | cfg['key'] = bytes_to_str(key) |
|
93 | cfg['transport'] = transport | |
|
84 | 94 | |
|
85 | 95 | with open(fname, 'w') as f: |
|
86 | 96 | f.write(json.dumps(cfg, indent=2)) |
@@ -179,8 +189,12 b' def base_launch_kernel(code, fname, stdin=None, stdout=None, stderr=None,' | |||
|
179 | 189 | creationflags=512, # CREATE_NEW_PROCESS_GROUP |
|
180 | 190 | stdin=_stdin, stdout=_stdout, stderr=_stderr) |
|
181 | 191 | else: |
|
182 | from _subprocess import DuplicateHandle, GetCurrentProcess, \ | |
|
183 | DUPLICATE_SAME_ACCESS | |
|
192 | try: | |
|
193 | from _winapi import DuplicateHandle, GetCurrentProcess, \ | |
|
194 | DUPLICATE_SAME_ACCESS | |
|
195 | except: | |
|
196 | from _subprocess import DuplicateHandle, GetCurrentProcess, \ | |
|
197 | DUPLICATE_SAME_ACCESS | |
|
184 | 198 | pid = GetCurrentProcess() |
|
185 | 199 | handle = DuplicateHandle(pid, pid, pid, 0, |
|
186 | 200 | True, # Inheritable by new processes. |
@@ -12,6 +12,7 b'' | |||
|
12 | 12 | # Imports |
|
13 | 13 | #----------------------------------------------------------------------------- |
|
14 | 14 | |
|
15 | import os | |
|
15 | 16 | import socket |
|
16 | 17 | import sys |
|
17 | 18 | from threading import Thread |
@@ -28,21 +29,28 b' from IPython.utils.localinterfaces import LOCALHOST' | |||
|
28 | 29 | class Heartbeat(Thread): |
|
29 | 30 | "A simple ping-pong style heartbeat that runs in a thread." |
|
30 | 31 | |
|
31 | def __init__(self, context, addr=(LOCALHOST, 0)): | |
|
32 | def __init__(self, context, addr=('tcp', LOCALHOST, 0)): | |
|
32 | 33 | Thread.__init__(self) |
|
33 | 34 | self.context = context |
|
34 | self.ip, self.port = addr | |
|
35 | self.transport, self.ip, self.port = addr | |
|
35 | 36 | if self.port == 0: |
|
36 | s = socket.socket() | |
|
37 | # '*' means all interfaces to 0MQ, which is '' to socket.socket | |
|
38 | s.bind(('' if self.ip == '*' else self.ip, 0)) | |
|
39 | self.port = s.getsockname()[1] | |
|
40 | s.close() | |
|
37 | if addr[0] == 'tcp': | |
|
38 | s = socket.socket() | |
|
39 | # '*' means all interfaces to 0MQ, which is '' to socket.socket | |
|
40 | s.bind(('' if self.ip == '*' else self.ip, 0)) | |
|
41 | self.port = s.getsockname()[1] | |
|
42 | s.close() | |
|
43 | elif addr[0] == 'ipc': | |
|
44 | while os.path.exists(self.ip + '-' + self.port): | |
|
45 | self.port = self.port + 1 | |
|
46 | else: | |
|
47 | raise ValueError("Unrecognized zmq transport: %s" % addr[0]) | |
|
41 | 48 | self.addr = (self.ip, self.port) |
|
42 | 49 | self.daemon = True |
|
43 | 50 | |
|
44 | 51 | def run(self): |
|
45 | 52 | self.socket = self.context.socket(zmq.REP) |
|
46 | self.socket.bind('tcp://%s:%i' % self.addr) | |
|
53 | c = ':' if self.transport == 'tcp' else '-' | |
|
54 | self.socket.bind('%s://%s' % (self.transport, self.ip) + c + str(self.port)) | |
|
47 | 55 | zmq.device(zmq.FORWARDER, self.socket, self.socket) |
|
48 | 56 |
@@ -4,7 +4,7 b' from io import StringIO' | |||
|
4 | 4 | |
|
5 | 5 | from session import extract_header, Message |
|
6 | 6 | |
|
7 |
from IPython.utils import io, text |
|
|
7 | from IPython.utils import io, text | |
|
8 | 8 | from IPython.utils import py3compat |
|
9 | 9 | |
|
10 | 10 | #----------------------------------------------------------------------------- |
@@ -23,6 +23,7 b' class OutStream(object):' | |||
|
23 | 23 | topic=None |
|
24 | 24 | |
|
25 | 25 | def __init__(self, session, pub_socket, name): |
|
26 | self.encoding = 'UTF-8' | |
|
26 | 27 | self.session = session |
|
27 | 28 | self.pub_socket = pub_socket |
|
28 | 29 | self.name = name |
@@ -73,9 +74,8 b' class OutStream(object):' | |||
|
73 | 74 | else: |
|
74 | 75 | # Make sure that we're handling unicode |
|
75 | 76 | if not isinstance(string, unicode): |
|
76 | enc = encoding.DEFAULT_ENCODING | |
|
77 | string = string.decode(enc, 'replace') | |
|
78 | ||
|
77 | string = string.decode(self.encoding, 'replace') | |
|
78 | ||
|
79 | 79 | self._buffer.write(string) |
|
80 | 80 | current_time = time.time() |
|
81 | 81 | if self._start <= 0: |
@@ -486,9 +486,10 b' class Kernel(Configurable):' | |||
|
486 | 486 | raw=raw, output=output) |
|
487 | 487 | |
|
488 | 488 | elif hist_access_type == 'search': |
|
489 | n = parent['content'].get('n') | |
|
489 | 490 | pattern = parent['content']['pattern'] |
|
490 | 491 | hist = self.shell.history_manager.search(pattern, raw=raw, |
|
491 |
output=output) |
|
|
492 | output=output, n=n) | |
|
492 | 493 | |
|
493 | 494 | else: |
|
494 | 495 | hist = [] |
@@ -35,8 +35,10 b' from IPython.utils import io' | |||
|
35 | 35 | from IPython.utils.localinterfaces import LOCALHOST |
|
36 | 36 | from IPython.utils.path import filefind |
|
37 | 37 | from IPython.utils.py3compat import str_to_bytes |
|
38 |
from IPython.utils.traitlets import ( |
|
|
39 | DottedObjectName) | |
|
38 | from IPython.utils.traitlets import ( | |
|
39 | Any, Instance, Dict, Unicode, Integer, Bool, CaselessStrEnum, | |
|
40 | DottedObjectName, | |
|
41 | ) | |
|
40 | 42 | from IPython.utils.importstring import import_item |
|
41 | 43 | # local imports |
|
42 | 44 | from IPython.zmq.entry_point import write_connection_file |
@@ -109,6 +111,7 b' class KernelApp(BaseIPythonApplication):' | |||
|
109 | 111 | self.config_file_specified = False |
|
110 | 112 | |
|
111 | 113 | # connection info: |
|
114 | transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True) | |
|
112 | 115 | ip = Unicode(LOCALHOST, config=True, |
|
113 | 116 | help="Set the IP or interface on which the kernel will listen.") |
|
114 | 117 | hb_port = Integer(0, config=True, help="set the heartbeat port [default: random]") |
@@ -154,11 +157,12 b' class KernelApp(BaseIPythonApplication):' | |||
|
154 | 157 | self.poller = ParentPollerUnix() |
|
155 | 158 | |
|
156 | 159 | def _bind_socket(self, s, port): |
|
157 |
iface = ' |
|
|
158 | if port <= 0: | |
|
160 | iface = '%s://%s' % (self.transport, self.ip) | |
|
161 | if port <= 0 and self.transport == 'tcp': | |
|
159 | 162 | port = s.bind_to_random_port(iface) |
|
160 | 163 | else: |
|
161 | s.bind(iface + ':%i'%port) | |
|
164 | c = ':' if self.transport == 'tcp' else '-' | |
|
165 | s.bind(iface + c + str(port)) | |
|
162 | 166 | return port |
|
163 | 167 | |
|
164 | 168 | def load_connection_file(self): |
@@ -174,6 +178,7 b' class KernelApp(BaseIPythonApplication):' | |||
|
174 | 178 | with open(fname) as f: |
|
175 | 179 | s = f.read() |
|
176 | 180 | cfg = json.loads(s) |
|
181 | self.transport = cfg.get('transport', self.transport) | |
|
177 | 182 | if self.ip == LOCALHOST and 'ip' in cfg: |
|
178 | 183 | # not overridden by config or cl_args |
|
179 | 184 | self.ip = cfg['ip'] |
@@ -191,7 +196,7 b' class KernelApp(BaseIPythonApplication):' | |||
|
191 | 196 | cf = os.path.join(self.profile_dir.security_dir, self.connection_file) |
|
192 | 197 | else: |
|
193 | 198 | cf = self.connection_file |
|
194 | write_connection_file(cf, ip=self.ip, key=self.session.key, | |
|
199 | write_connection_file(cf, ip=self.ip, key=self.session.key, transport=self.transport, | |
|
195 | 200 | shell_port=self.shell_port, stdin_port=self.stdin_port, hb_port=self.hb_port, |
|
196 | 201 | iopub_port=self.iopub_port) |
|
197 | 202 | |
@@ -204,6 +209,19 b' class KernelApp(BaseIPythonApplication):' | |||
|
204 | 209 | os.remove(cf) |
|
205 | 210 | except (IOError, OSError): |
|
206 | 211 | pass |
|
212 | ||
|
213 | self._cleanup_ipc_files() | |
|
214 | ||
|
215 | def _cleanup_ipc_files(self): | |
|
216 | """cleanup ipc files if we wrote them""" | |
|
217 | if self.transport != 'ipc': | |
|
218 | return | |
|
219 | for port in (self.shell_port, self.iopub_port, self.stdin_port, self.hb_port): | |
|
220 | ipcfile = "%s-%i" % (self.ip, port) | |
|
221 | try: | |
|
222 | os.remove(ipcfile) | |
|
223 | except (IOError, OSError): | |
|
224 | pass | |
|
207 | 225 | |
|
208 | 226 | def init_connection_file(self): |
|
209 | 227 | if not self.connection_file: |
@@ -238,7 +256,7 b' class KernelApp(BaseIPythonApplication):' | |||
|
238 | 256 | # heartbeat doesn't share context, because it mustn't be blocked |
|
239 | 257 | # by the GIL, which is accessed by libzmq when freeing zero-copy messages |
|
240 | 258 | hb_ctx = zmq.Context() |
|
241 | self.heartbeat = Heartbeat(hb_ctx, (self.ip, self.hb_port)) | |
|
259 | self.heartbeat = Heartbeat(hb_ctx, (self.transport, self.ip, self.hb_port)) | |
|
242 | 260 | self.hb_port = self.heartbeat.port |
|
243 | 261 | self.log.debug("Heartbeat REP Channel on port: %i"%self.hb_port) |
|
244 | 262 | self.heartbeat.start() |
@@ -37,7 +37,7 b' from zmq.eventloop import ioloop, zmqstream' | |||
|
37 | 37 | from IPython.config.loader import Config |
|
38 | 38 | from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS |
|
39 | 39 | from IPython.utils.traitlets import ( |
|
40 | HasTraits, Any, Instance, Type, Unicode, Integer, Bool | |
|
40 | HasTraits, Any, Instance, Type, Unicode, Integer, Bool, CaselessStrEnum | |
|
41 | 41 | ) |
|
42 | 42 | from IPython.utils.py3compat import str_to_bytes |
|
43 | 43 | from IPython.zmq.entry_point import write_connection_file |
@@ -103,7 +103,7 b' class ZMQSocketChannel(Thread):' | |||
|
103 | 103 | The ZMQ context to use. |
|
104 | 104 | session : :class:`session.Session` |
|
105 | 105 | The session to use. |
|
106 |
address : |
|
|
106 | address : zmq url | |
|
107 | 107 | Standard (ip, port) tuple that the kernel is listening on. |
|
108 | 108 | """ |
|
109 | 109 | super(ZMQSocketChannel, self).__init__() |
@@ -111,9 +111,11 b' class ZMQSocketChannel(Thread):' | |||
|
111 | 111 | |
|
112 | 112 | self.context = context |
|
113 | 113 | self.session = session |
|
114 |
if address |
|
|
115 | message = 'The port number for a channel cannot be 0.' | |
|
116 | raise InvalidPortNumber(message) | |
|
114 | if isinstance(address, tuple): | |
|
115 | if address[1] == 0: | |
|
116 | message = 'The port number for a channel cannot be 0.' | |
|
117 | raise InvalidPortNumber(message) | |
|
118 | address = "tcp://%s:%i" % address | |
|
117 | 119 | self._address = address |
|
118 | 120 | atexit.register(self._notice_exit) |
|
119 | 121 | |
@@ -149,10 +151,7 b' class ZMQSocketChannel(Thread):' | |||
|
149 | 151 | |
|
150 | 152 | @property |
|
151 | 153 | def address(self): |
|
152 |
"""Get the channel's address as a |
|
|
153 | ||
|
154 | By the default, the address is (localhost, 0), where 0 means a random | |
|
155 | port. | |
|
154 | """Get the channel's address as a zmq url string ('tcp://127.0.0.1:5555'). | |
|
156 | 155 | """ |
|
157 | 156 | return self._address |
|
158 | 157 | |
@@ -196,7 +195,7 b' class ShellSocketChannel(ZMQSocketChannel):' | |||
|
196 | 195 | """The thread's main activity. Call start() instead.""" |
|
197 | 196 | self.socket = self.context.socket(zmq.DEALER) |
|
198 | 197 | self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) |
|
199 |
self.socket.connect( |
|
|
198 | self.socket.connect(self.address) | |
|
200 | 199 | self.stream = zmqstream.ZMQStream(self.socket, self.ioloop) |
|
201 | 200 | self.stream.on_recv(self._handle_recv) |
|
202 | 201 | self._run_loop() |
@@ -242,15 +241,16 b' class ShellSocketChannel(ZMQSocketChannel):' | |||
|
242 | 241 | :func:`repr` as values. |
|
243 | 242 | |
|
244 | 243 | user_expressions : dict, optional |
|
245 |
A dict |
|
|
246 | namespace. They will come back as a dict with these names as keys | |
|
247 |
|
|
|
244 | A dict mapping names to expressions to be evaluated in the user's | |
|
245 | dict. The expression values are returned as strings formatted using | |
|
246 | :func:`repr`. | |
|
247 | ||
|
248 | allow_stdin : bool, optional (default self.allow_stdin) | |
|
249 | Flag for whether the kernel can send stdin requests to frontends. | |
|
248 | 250 | |
|
249 | allow_stdin : bool, optional | |
|
250 | Flag for | |
|
251 | A dict with string keys and to pull from the user's | |
|
252 | namespace. They will come back as a dict with these names as keys | |
|
253 | and their :func:`repr` as values. | |
|
251 | Some frontends (e.g. the Notebook) do not support stdin requests. | |
|
252 | If raw_input is called from code executed from such a frontend, a | |
|
253 | StdinNotImplementedError will be raised. | |
|
254 | 254 | |
|
255 | 255 | Returns |
|
256 | 256 | ------- |
@@ -395,7 +395,7 b' class SubSocketChannel(ZMQSocketChannel):' | |||
|
395 | 395 | self.socket = self.context.socket(zmq.SUB) |
|
396 | 396 | self.socket.setsockopt(zmq.SUBSCRIBE,b'') |
|
397 | 397 | self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) |
|
398 |
self.socket.connect( |
|
|
398 | self.socket.connect(self.address) | |
|
399 | 399 | self.stream = zmqstream.ZMQStream(self.socket, self.ioloop) |
|
400 | 400 | self.stream.on_recv(self._handle_recv) |
|
401 | 401 | self._run_loop() |
@@ -461,7 +461,7 b' class StdInSocketChannel(ZMQSocketChannel):' | |||
|
461 | 461 | """The thread's main activity. Call start() instead.""" |
|
462 | 462 | self.socket = self.context.socket(zmq.DEALER) |
|
463 | 463 | self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) |
|
464 |
self.socket.connect( |
|
|
464 | self.socket.connect(self.address) | |
|
465 | 465 | self.stream = zmqstream.ZMQStream(self.socket, self.ioloop) |
|
466 | 466 | self.stream.on_recv(self._handle_recv) |
|
467 | 467 | self._run_loop() |
@@ -520,7 +520,7 b' class HBSocketChannel(ZMQSocketChannel):' | |||
|
520 | 520 | self.socket.close() |
|
521 | 521 | self.socket = self.context.socket(zmq.REQ) |
|
522 | 522 | self.socket.setsockopt(zmq.LINGER, 0) |
|
523 |
self.socket.connect( |
|
|
523 | self.socket.connect(self.address) | |
|
524 | 524 | |
|
525 | 525 | self.poller.register(self.socket, zmq.POLLIN) |
|
526 | 526 | |
@@ -659,6 +659,10 b' class KernelManager(HasTraits):' | |||
|
659 | 659 | |
|
660 | 660 | # The addresses for the communication channels. |
|
661 | 661 | connection_file = Unicode('') |
|
662 | ||
|
663 | transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp') | |
|
664 | ||
|
665 | ||
|
662 | 666 | ip = Unicode(LOCALHOST) |
|
663 | 667 | def _ip_changed(self, name, old, new): |
|
664 | 668 | if new == '*': |
@@ -747,7 +751,20 b' class KernelManager(HasTraits):' | |||
|
747 | 751 | self._connection_file_written = False |
|
748 | 752 | try: |
|
749 | 753 | os.remove(self.connection_file) |
|
750 | except OSError: | |
|
754 | except (IOError, OSError): | |
|
755 | pass | |
|
756 | ||
|
757 | self._cleanup_ipc_files() | |
|
758 | ||
|
759 | def _cleanup_ipc_files(self): | |
|
760 | """cleanup ipc files if we wrote them""" | |
|
761 | if self.transport != 'ipc': | |
|
762 | return | |
|
763 | for port in (self.shell_port, self.iopub_port, self.stdin_port, self.hb_port): | |
|
764 | ipcfile = "%s-%i" % (self.ip, port) | |
|
765 | try: | |
|
766 | os.remove(ipcfile) | |
|
767 | except (IOError, OSError): | |
|
751 | 768 | pass |
|
752 | 769 | |
|
753 | 770 | def load_connection_file(self): |
@@ -755,6 +772,9 b' class KernelManager(HasTraits):' | |||
|
755 | 772 | with open(self.connection_file) as f: |
|
756 | 773 | cfg = json.loads(f.read()) |
|
757 | 774 | |
|
775 | from pprint import pprint | |
|
776 | pprint(cfg) | |
|
777 | self.transport = cfg.get('transport', 'tcp') | |
|
758 | 778 | self.ip = cfg['ip'] |
|
759 | 779 | self.shell_port = cfg['shell_port'] |
|
760 | 780 | self.stdin_port = cfg['stdin_port'] |
@@ -767,7 +787,7 b' class KernelManager(HasTraits):' | |||
|
767 | 787 | if self._connection_file_written: |
|
768 | 788 | return |
|
769 | 789 | self.connection_file,cfg = write_connection_file(self.connection_file, |
|
770 | ip=self.ip, key=self.session.key, | |
|
790 | transport=self.transport, ip=self.ip, key=self.session.key, | |
|
771 | 791 | stdin_port=self.stdin_port, iopub_port=self.iopub_port, |
|
772 | 792 | shell_port=self.shell_port, hb_port=self.hb_port) |
|
773 | 793 | # write_connection_file also sets default ports: |
@@ -794,7 +814,7 b' class KernelManager(HasTraits):' | |||
|
794 | 814 | **kw : optional |
|
795 | 815 | See respective options for IPython and Python kernels. |
|
796 | 816 | """ |
|
797 | if self.ip not in LOCAL_IPS: | |
|
817 | if self.transport == 'tcp' and self.ip not in LOCAL_IPS: | |
|
798 | 818 | raise RuntimeError("Can only launch a kernel on a local interface. " |
|
799 | 819 | "Make sure that the '*_address' attributes are " |
|
800 | 820 | "configured properly. " |
@@ -811,8 +831,9 b' class KernelManager(HasTraits):' | |||
|
811 | 831 | self.kernel = launch_kernel(fname=self.connection_file, **kw) |
|
812 | 832 | |
|
813 | 833 | def shutdown_kernel(self, restart=False): |
|
814 |
""" Attempts to the stop the kernel process cleanly. |
|
|
815 | cannot be stopped, it is killed, if possible. | |
|
834 | """ Attempts to the stop the kernel process cleanly. | |
|
835 | ||
|
836 | If the kernel cannot be stopped and the kernel is local, it is killed. | |
|
816 | 837 | """ |
|
817 | 838 | # FIXME: Shutdown does not work on Windows due to ZMQ errors! |
|
818 | 839 | if sys.platform == 'win32': |
@@ -893,13 +914,17 b' class KernelManager(HasTraits):' | |||
|
893 | 914 | return self.kernel is not None |
|
894 | 915 | |
|
895 | 916 | def kill_kernel(self): |
|
896 |
""" Kill the running kernel. |
|
|
917 | """ Kill the running kernel. | |
|
918 | ||
|
919 | This method blocks until the kernel process has terminated. | |
|
920 | """ | |
|
897 | 921 | if self.has_kernel: |
|
898 | 922 | # Pause the heart beat channel if it exists. |
|
899 | 923 | if self._hb_channel is not None: |
|
900 | 924 | self._hb_channel.pause() |
|
901 | 925 | |
|
902 | # Attempt to kill the kernel. | |
|
926 | # Signal the kernel to terminate (sends SIGKILL on Unix and calls | |
|
927 | # TerminateProcess() on Win32). | |
|
903 | 928 | try: |
|
904 | 929 | self.kernel.kill() |
|
905 | 930 | except OSError as e: |
@@ -914,13 +939,18 b' class KernelManager(HasTraits):' | |||
|
914 | 939 | from errno import ESRCH |
|
915 | 940 | if e.errno != ESRCH: |
|
916 | 941 | raise |
|
942 | ||
|
943 | # Block until the kernel terminates. | |
|
944 | self.kernel.wait() | |
|
917 | 945 | self.kernel = None |
|
918 | 946 | else: |
|
919 | 947 | raise RuntimeError("Cannot kill kernel. No kernel is running!") |
|
920 | 948 | |
|
921 | 949 | def interrupt_kernel(self): |
|
922 |
""" Interrupts the kernel. |
|
|
923 | well supported on all platforms. | |
|
950 | """ Interrupts the kernel. | |
|
951 | ||
|
952 | Unlike ``signal_kernel``, this operation is well supported on all | |
|
953 | platforms. | |
|
924 | 954 | """ |
|
925 | 955 | if self.has_kernel: |
|
926 | 956 | if sys.platform == 'win32': |
@@ -932,8 +962,10 b' class KernelManager(HasTraits):' | |||
|
932 | 962 | raise RuntimeError("Cannot interrupt kernel. No kernel is running!") |
|
933 | 963 | |
|
934 | 964 | def signal_kernel(self, signum): |
|
935 |
""" Sends a signal to the kernel. |
|
|
936 | supported on Windows, this function is only useful on Unix systems. | |
|
965 | """ Sends a signal to the kernel. | |
|
966 | ||
|
967 | Note that since only SIGTERM is supported on Windows, this function is | |
|
968 | only useful on Unix systems. | |
|
937 | 969 | """ |
|
938 | 970 | if self.has_kernel: |
|
939 | 971 | self.kernel.send_signal(signum) |
@@ -961,13 +993,21 b' class KernelManager(HasTraits):' | |||
|
961 | 993 | # Channels used for communication with the kernel: |
|
962 | 994 | #-------------------------------------------------------------------------- |
|
963 | 995 | |
|
996 | def _make_url(self, port): | |
|
997 | """make a zmq url with a port""" | |
|
998 | if self.transport == 'tcp': | |
|
999 | return "tcp://%s:%i" % (self.ip, port) | |
|
1000 | else: | |
|
1001 | return "%s://%s-%s" % (self.transport, self.ip, port) | |
|
1002 | ||
|
964 | 1003 | @property |
|
965 | 1004 | def shell_channel(self): |
|
966 | 1005 | """Get the REQ socket channel object to make requests of the kernel.""" |
|
967 | 1006 | if self._shell_channel is None: |
|
968 | 1007 | self._shell_channel = self.shell_channel_class(self.context, |
|
969 | self.session, | |
|
970 |
|
|
|
1008 | self.session, | |
|
1009 | self._make_url(self.shell_port), | |
|
1010 | ) | |
|
971 | 1011 | return self._shell_channel |
|
972 | 1012 | |
|
973 | 1013 | @property |
@@ -976,7 +1016,8 b' class KernelManager(HasTraits):' | |||
|
976 | 1016 | if self._sub_channel is None: |
|
977 | 1017 | self._sub_channel = self.sub_channel_class(self.context, |
|
978 | 1018 | self.session, |
|
979 |
|
|
|
1019 | self._make_url(self.iopub_port), | |
|
1020 | ) | |
|
980 | 1021 | return self._sub_channel |
|
981 | 1022 | |
|
982 | 1023 | @property |
@@ -984,8 +1025,9 b' class KernelManager(HasTraits):' | |||
|
984 | 1025 | """Get the REP socket channel object to handle stdin (raw_input).""" |
|
985 | 1026 | if self._stdin_channel is None: |
|
986 | 1027 | self._stdin_channel = self.stdin_channel_class(self.context, |
|
987 | self.session, | |
|
988 |
|
|
|
1028 | self.session, | |
|
1029 | self._make_url(self.stdin_port), | |
|
1030 | ) | |
|
989 | 1031 | return self._stdin_channel |
|
990 | 1032 | |
|
991 | 1033 | @property |
@@ -994,6 +1036,7 b' class KernelManager(HasTraits):' | |||
|
994 | 1036 | kernel is alive.""" |
|
995 | 1037 | if self._hb_channel is None: |
|
996 | 1038 | self._hb_channel = self.hb_channel_class(self.context, |
|
997 |
|
|
|
998 |
|
|
|
1039 | self.session, | |
|
1040 | self._make_url(self.hb_port), | |
|
1041 | ) | |
|
999 | 1042 | return self._hb_channel |
@@ -100,7 +100,10 b' class ParentPollerWindows(Thread):' | |||
|
100 | 100 | def run(self): |
|
101 | 101 | """ Run the poll loop. This method never returns. |
|
102 | 102 | """ |
|
103 | from _subprocess import WAIT_OBJECT_0, INFINITE | |
|
103 | try: | |
|
104 | from _winapi import WAIT_OBJECT_0, INFINITE | |
|
105 | except ImportError: | |
|
106 | from _subprocess import WAIT_OBJECT_0, INFINITE | |
|
104 | 107 | |
|
105 | 108 | # Build the list of handle to listen on. |
|
106 | 109 | handles = [] |
@@ -10,7 +10,7 b' import sys' | |||
|
10 | 10 | |
|
11 | 11 | # Third-party imports |
|
12 | 12 | import matplotlib |
|
13 | from matplotlib.backends.backend_agg import new_figure_manager | |
|
13 | from matplotlib.backends.backend_agg import new_figure_manager, FigureCanvasAgg | |
|
14 | 14 | from matplotlib._pylab_helpers import Gcf |
|
15 | 15 | |
|
16 | 16 | # Local imports. |
@@ -207,3 +207,9 b' def flush_figures():' | |||
|
207 | 207 | show._to_draw = [] |
|
208 | 208 | show._draw_called = False |
|
209 | 209 | |
|
210 | ||
|
211 | # Changes to matplotlib in version 1.2 requires a mpl backend to supply a default | |
|
212 | # figurecanvas. This is set here to a Agg canvas | |
|
213 | # See https://github.com/matplotlib/matplotlib/pull/1125 | |
|
214 | FigureCanvas = FigureCanvasAgg | |
|
215 |
@@ -43,6 +43,7 b' from zmq.utils import jsonapi' | |||
|
43 | 43 | from zmq.eventloop.ioloop import IOLoop |
|
44 | 44 | from zmq.eventloop.zmqstream import ZMQStream |
|
45 | 45 | |
|
46 | import IPython | |
|
46 | 47 | from IPython.config.application import Application, boolean_flag |
|
47 | 48 | from IPython.config.configurable import Configurable, LoggingConfigurable |
|
48 | 49 | from IPython.utils.importstring import import_item |
@@ -74,7 +75,7 b' def squash_unicode(obj):' | |||
|
74 | 75 | # globals and defaults |
|
75 | 76 | #----------------------------------------------------------------------------- |
|
76 | 77 | |
|
77 | ||
|
78 | _version_info_list = list(IPython.version_info) | |
|
78 | 79 | # ISO8601-ify datetime objects |
|
79 | 80 | json_packer = lambda obj: jsonapi.dumps(obj, default=date_default) |
|
80 | 81 | json_unpacker = lambda s: extract_dates(jsonapi.loads(s)) |
@@ -187,6 +188,7 b' class Message(object):' | |||
|
187 | 188 | |
|
188 | 189 | def msg_header(msg_id, msg_type, username, session): |
|
189 | 190 | date = datetime.now() |
|
191 | version = _version_info_list | |
|
190 | 192 | return locals() |
|
191 | 193 | |
|
192 | 194 | def extract_header(msg_or_header): |
@@ -38,7 +38,7 b' from IPython.lib.kernel import (' | |||
|
38 | 38 | get_connection_file, get_connection_info, connect_qtconsole |
|
39 | 39 | ) |
|
40 | 40 | from IPython.testing.skipdoctest import skip_doctest |
|
41 | from IPython.utils import io | |
|
41 | from IPython.utils import io, openpy | |
|
42 | 42 | from IPython.utils.jsonutil import json_clean, encode_images |
|
43 | 43 | from IPython.utils.process import arg_split |
|
44 | 44 | from IPython.utils import py3compat |
@@ -355,7 +355,9 b' class KernelMagics(Magics):' | |||
|
355 | 355 | |
|
356 | 356 | cont = open(arg_s).read() |
|
357 | 357 | if arg_s.endswith('.py'): |
|
358 |
cont = self.shell.pycolorize( |
|
|
358 | cont = self.shell.pycolorize(openpy.read_py_file(arg_s, skip_encoding_cookie=False)) | |
|
359 | else: | |
|
360 | cont = open(arg_s).read() | |
|
359 | 361 | page.page(cont) |
|
360 | 362 | |
|
361 | 363 | more = line_magic('more')(less) |
@@ -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 |
@@ -1,9 +1,12 b'' | |||
|
1 | # -*- coding: utf-8 -*- | |
|
1 | 2 | """A simple interactive demo to illustrate the use of IPython's Demo class. |
|
2 | 3 | |
|
3 | 4 | Any python script can be run as a demo, but that does little more than showing |
|
4 | 5 | it on-screen, syntax-highlighted in one shot. If you add a little simple |
|
5 | 6 | markup, you can stop at specified intervals and return to the ipython prompt, |
|
6 | 7 | resuming execution later. |
|
8 | ||
|
9 | This is a unicode test, åäö | |
|
7 | 10 | """ |
|
8 | 11 | from __future__ import print_function |
|
9 | 12 |
@@ -204,25 +204,16 b'' | |||
|
204 | 204 | "collapsed": true, |
|
205 | 205 | "input": [ |
|
206 | 206 | "import sys, time\n", |
|
207 | "try:\n", | |
|
208 | " from IPython.display import clear_output\n", | |
|
209 | " have_ipython = True\n", | |
|
210 | "except ImportError:\n", | |
|
211 | " have_ipython = False\n", | |
|
212 | 207 | "\n", |
|
213 | 208 | "class ProgressBar:\n", |
|
214 | 209 | " def __init__(self, iterations):\n", |
|
215 | 210 | " self.iterations = iterations\n", |
|
216 | 211 | " self.prog_bar = '[]'\n", |
|
217 | 212 | " self.fill_char = '*'\n", |
|
218 |
" self.width = |
|
|
213 | " self.width = 50\n", | |
|
219 | 214 | " self.__update_amount(0)\n", |
|
220 | " if have_ipython:\n", | |
|
221 | " self.animate = self.animate_ipython\n", | |
|
222 | " else:\n", | |
|
223 | " self.animate = self.animate_noipython\n", | |
|
224 | 215 | "\n", |
|
225 |
" def animate |
|
|
216 | " def animate(self, iter):\n", | |
|
226 | 217 | " print '\\r', self,\n", |
|
227 | 218 | " sys.stdout.flush()\n", |
|
228 | 219 | " self.update_iteration(iter + 1)\n", |
@@ -254,19 +245,12 b'' | |||
|
254 | 245 | "input": [ |
|
255 | 246 | "p = ProgressBar(1000)\n", |
|
256 | 247 | "for i in range(1001):\n", |
|
248 | " time.sleep(0.002)\n", | |
|
257 | 249 | " p.animate(i)" |
|
258 | 250 | ], |
|
259 | 251 | "language": "python", |
|
260 | 252 | "metadata": {}, |
|
261 | 253 | "outputs": [] |
|
262 | }, | |
|
263 | { | |
|
264 | "cell_type": "code", | |
|
265 | "collapsed": false, | |
|
266 | "input": [], | |
|
267 | "language": "python", | |
|
268 | "metadata": {}, | |
|
269 | "outputs": [] | |
|
270 | 254 | } |
|
271 | 255 | ], |
|
272 | 256 | "metadata": {} |
@@ -24,26 +24,7 b' if ON_RTD:' | |||
|
24 | 24 | # see |
|
25 | 25 | # http://read-the-docs.readthedocs.org/en/latest/faq.html |
|
26 | 26 | tags.add('rtd') |
|
27 | class Mock(object): | |
|
28 | def __init__(self, *args, **kwargs): | |
|
29 | pass | |
|
30 | ||
|
31 | def __call__(self, *args, **kwargs): | |
|
32 | return Mock() | |
|
33 | ||
|
34 | @classmethod | |
|
35 | def __getattr__(self, name): | |
|
36 | if name in ('__file__', '__path__'): | |
|
37 | return '/dev/null' | |
|
38 | elif name[0] == name[0].upper(): | |
|
39 | return type(name, (), {}) | |
|
40 | else: | |
|
41 | return Mock() | |
|
42 | ||
|
43 | MOCK_MODULES = ['matplotlib', 'matplotlib.sphinxext', 'numpy'] | |
|
44 | for mod_name in MOCK_MODULES: | |
|
45 | sys.modules[mod_name] = Mock() | |
|
46 | ||
|
27 | ||
|
47 | 28 | # If your extensions are in another directory, add it here. If the directory |
|
48 | 29 | # is relative to the documentation root, use os.path.abspath to make it |
|
49 | 30 | # absolute, like shown here. |
@@ -63,9 +44,9 b" execfile('../../IPython/core/release.py',iprelease)" | |||
|
63 | 44 | # Add any Sphinx extension module names here, as strings. They can be extensions |
|
64 | 45 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. |
|
65 | 46 | extensions = [ |
|
66 |
|
|
|
47 | 'matplotlib.sphinxext.mathmpl', | |
|
67 | 48 | 'matplotlib.sphinxext.only_directives', |
|
68 |
|
|
|
49 | 'matplotlib.sphinxext.plot_directive', | |
|
69 | 50 | 'sphinx.ext.autodoc', |
|
70 | 51 | 'sphinx.ext.doctest', |
|
71 | 52 | 'inheritance_diagram', |
@@ -43,7 +43,7 b' functions to load and unload it. Here is a template::' | |||
|
43 | 43 | def load_ipython_extension(ipython): |
|
44 | 44 | # The `ipython` argument is the currently active `InteractiveShell` |
|
45 | 45 | # instance, which can be used in any way. This allows you to register |
|
46 |
# new magics |
|
|
46 | # new magics or aliases, for example. | |
|
47 | 47 | |
|
48 | 48 | def unload_ipython_extension(ipython): |
|
49 | 49 | # If you want your extension to be unloadable, put that logic here. |
@@ -70,29 +70,6 b' write extensions, you can also put your extensions in' | |||
|
70 | 70 | When your extension is ready for general use, please add it to the `extensions |
|
71 | 71 | index <http://wiki.ipython.org/Extensions_Index>`_. |
|
72 | 72 | |
|
73 | Plugin class | |
|
74 | ------------ | |
|
75 | ||
|
76 | More advanced extensions might want to subclass :class:`IPython.core.plugin.Plugin`. | |
|
77 | A plugin can have options configured by IPython's main :ref:`configuration | |
|
78 | system <config_overview>`. The code to load and unload it looks like this:: | |
|
79 | ||
|
80 | def load_ipython_extension(ip): | |
|
81 | """Load the plugin in IPython.""" | |
|
82 | plugin = MyPlugin(shell=ip, config=ip.config) | |
|
83 | try: | |
|
84 | ip.plugin_manager.register_plugin('myplugin', plugin) | |
|
85 | except KeyError: | |
|
86 | print("Already loaded") | |
|
87 | ||
|
88 | def unload_ipython_extension(ip): | |
|
89 | ip.plugin_manager.unregister_plugin('myplugin') | |
|
90 | ||
|
91 | For examples, see these files: | |
|
92 | ||
|
93 | * :file:`IPython/extensions/autoreload.py` | |
|
94 | * :file:`IPython/extensions/storemagic.py` | |
|
95 | ||
|
96 | 73 | .. _bundled_extensions: |
|
97 | 74 | |
|
98 | 75 | Extensions bundled with IPython |
@@ -627,7 +627,7 b' Message type: ``history_request``::' | |||
|
627 | 627 | 'start' : int, |
|
628 | 628 | 'stop' : int, |
|
629 | 629 | |
|
630 | # If hist_access_type is 'tail', get the last n cells. | |
|
630 | # If hist_access_type is 'tail' or 'search', get the last n cells. | |
|
631 | 631 | 'n' : int, |
|
632 | 632 | |
|
633 | 633 | # If hist_access_type is 'search', get cells matching the specified glob |
@@ -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. |
@@ -386,19 +386,20 b' weighted: Weighted Two-Bin Random' | |||
|
386 | 386 | Greedy Assignment |
|
387 | 387 | ----------------- |
|
388 | 388 | |
|
389 |
Tasks |
|
|
389 | Tasks can be assigned greedily as they are submitted. If their dependencies are | |
|
390 | 390 | met, they will be assigned to an engine right away, and multiple tasks can be |
|
391 | 391 | assigned to an engine at a given time. This limit is set with the |
|
392 |
``TaskScheduler.hwm`` (high water mark) configurable |
|
|
392 | ``TaskScheduler.hwm`` (high water mark) configurable in your | |
|
393 | :file:`ipcontroller_config.py` config file, with: | |
|
393 | 394 | |
|
394 | 395 | .. sourcecode:: python |
|
395 | 396 | |
|
396 | 397 | # the most common choices are: |
|
397 |
c.TaskSheduler.hwm = 0 # (minimal latency, default in IPython |
|
|
398 | c.TaskSheduler.hwm = 0 # (minimal latency, default in IPython < 0.13) | |
|
398 | 399 | # or |
|
399 |
c.TaskScheduler.hwm = 1 # (most-informed balancing, default in |
|
|
400 | c.TaskScheduler.hwm = 1 # (most-informed balancing, default in ≥ 0.13) | |
|
400 | 401 | |
|
401 |
In IPython |
|
|
402 | In IPython < 0.13, the default is 0, or no-limit. That is, there is no limit to the number of | |
|
402 | 403 | tasks that can be outstanding on a given engine. This greatly benefits the |
|
403 | 404 | latency of execution, because network traffic can be hidden behind computation. |
|
404 | 405 | However, this means that workload is assigned without knowledge of how long |
@@ -406,10 +407,10 b' each task might take, and can result in poor load-balancing, particularly for' | |||
|
406 | 407 | submitting a collection of heterogeneous tasks all at once. You can limit this |
|
407 | 408 | effect by setting hwm to a positive integer, 1 being maximum load-balancing (a |
|
408 | 409 | task will never be waiting if there is an idle engine), and any larger number |
|
409 |
being a compromise between load-balanc |
|
|
410 | being a compromise between load-balancing and latency-hiding. | |
|
410 | 411 | |
|
411 | 412 | In practice, some users have been confused by having this optimization on by |
|
412 |
default, |
|
|
413 | default, so the default value has been changed to 1 in IPython 0.13. This can be slower, | |
|
413 | 414 | but has more obvious behavior and won't result in assigning too many tasks to |
|
414 | 415 | some engines in heterogeneous cases. |
|
415 | 416 |
@@ -230,6 +230,7 b" if 'setuptools' in sys.modules:" | |||
|
230 | 230 | setuptools_extra_args['entry_points'] = find_scripts(True) |
|
231 | 231 | setup_args['extras_require'] = dict( |
|
232 | 232 | parallel = 'pyzmq>=2.1.4', |
|
233 | qtconsole = 'pygments', | |
|
233 | 234 | zmq = 'pyzmq>=2.1.4', |
|
234 | 235 | doc = 'Sphinx>=0.3', |
|
235 | 236 | test = 'nose>=0.10.1', |
@@ -204,12 +204,12 b' def find_data_files():' | |||
|
204 | 204 | manpagebase = pjoin('share', 'man', 'man1') |
|
205 | 205 | |
|
206 | 206 | # Simple file lists can be made by hand |
|
207 |
manpages |
|
|
207 | manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)] | |
|
208 | 208 | if not manpages: |
|
209 | 209 | # When running from a source tree, the manpages aren't gzipped |
|
210 |
manpages = |
|
|
211 | igridhelpfiles = filter(isfile, | |
|
212 |
|
|
|
210 | manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)] | |
|
211 | ||
|
212 | igridhelpfiles = [f for f in glob(pjoin('IPython','extensions','igrid_help.*')) if isfile(f)] | |
|
213 | 213 | |
|
214 | 214 | # For nested structures, use the utility above |
|
215 | 215 | example_files = make_dir_struct( |
@@ -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] |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now