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 | docs/gh-pages |
|
7 | docs/gh-pages | |
8 | IPython/frontend/html/notebook/static/mathjax |
|
8 | IPython/frontend/html/notebook/static/mathjax | |
9 | *.py[co] |
|
9 | *.py[co] | |
|
10 | __pycache__ | |||
10 | build |
|
11 | build | |
11 | *.egg-info |
|
12 | *.egg-info | |
12 | *~ |
|
13 | *~ |
@@ -57,6 +57,7 b' for author, email in release.authors.itervalues():' | |||||
57 | __author__ += author + ' <' + email + '>\n' |
|
57 | __author__ += author + ' <' + email + '>\n' | |
58 | __license__ = release.license |
|
58 | __license__ = release.license | |
59 | __version__ = release.version |
|
59 | __version__ = release.version | |
|
60 | version_info = release.version_info | |||
60 |
|
61 | |||
61 | def embed_kernel(module=None, local_ns=None, **kwargs): |
|
62 | def embed_kernel(module=None, local_ns=None, **kwargs): | |
62 | """Embed and start an IPython kernel in a given scope. |
|
63 | """Embed and start an IPython kernel in a given scope. |
@@ -181,7 +181,7 b' class BaseIPythonApplication(Application):' | |||||
181 | sys.path.remove(old) |
|
181 | sys.path.remove(old) | |
182 | sys.path.append(os.path.abspath(new)) |
|
182 | sys.path.append(os.path.abspath(new)) | |
183 | if not os.path.isdir(new): |
|
183 | if not os.path.isdir(new): | |
184 | os.makedirs(new, mode=0777) |
|
184 | os.makedirs(new, mode=0o777) | |
185 | readme = os.path.join(new, 'README') |
|
185 | readme = os.path.join(new, 'README') | |
186 | if not os.path.exists(readme): |
|
186 | if not os.path.exists(readme): | |
187 | path = os.path.join(get_ipython_package_dir(), u'config', u'profile') |
|
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 | # Stdlib imports |
|
18 | # Stdlib imports | |
19 | import glob |
|
19 | import glob | |
|
20 | import imp | |||
20 | import inspect |
|
21 | import inspect | |
21 | import os |
|
22 | import os | |
22 | import re |
|
23 | import re | |
@@ -90,7 +91,8 b' def module_list(path):' | |||||
90 |
|
91 | |||
91 | # Now find actual path matches for packages or modules |
|
92 | # Now find actual path matches for packages or modules | |
92 | folder_list = [p for p in folder_list |
|
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 | or is_importable_file(p) ] |
|
96 | or is_importable_file(p) ] | |
95 |
|
97 | |||
96 | return [basename(p).split('.')[0] for p in folder_list] |
|
98 | return [basename(p).split('.')[0] for p in folder_list] |
@@ -30,9 +30,9 b' import bdb' | |||||
30 | import linecache |
|
30 | import linecache | |
31 | import sys |
|
31 | import sys | |
32 |
|
32 | |||
33 | from IPython.utils import PyColorize |
|
33 | from IPython.utils import PyColorize, ulinecache | |
34 | from IPython.core import ipapi |
|
34 | from IPython.core import ipapi | |
35 | from IPython.utils import coloransi, io |
|
35 | from IPython.utils import coloransi, io, openpy, py3compat | |
36 | from IPython.core.excolors import exception_colors |
|
36 | from IPython.core.excolors import exception_colors | |
37 |
|
37 | |||
38 | # See if we can use pydb. |
|
38 | # See if we can use pydb. | |
@@ -304,16 +304,16 b' class Pdb(OldPdb):' | |||||
304 | # vds: << |
|
304 | # vds: << | |
305 |
|
305 | |||
306 | def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3): |
|
306 | def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3): | |
307 |
import |
|
307 | import repr | |
308 |
|
308 | |||
309 | ret = [] |
|
309 | ret = [] | |
310 |
|
310 | |||
311 | Colors = self.color_scheme_table.active_colors |
|
311 | Colors = self.color_scheme_table.active_colors | |
312 | ColorsNormal = Colors.Normal |
|
312 | ColorsNormal = Colors.Normal | |
313 | tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal) |
|
313 | tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal) | |
314 | tpl_call = '%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal) |
|
314 | tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal) | |
315 | tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) |
|
315 | tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) | |
316 | tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, |
|
316 | tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, | |
317 | ColorsNormal) |
|
317 | ColorsNormal) | |
318 |
|
318 | |||
319 | frame, lineno = frame_lineno |
|
319 | frame, lineno = frame_lineno | |
@@ -327,7 +327,7 b' class Pdb(OldPdb):' | |||||
327 |
|
327 | |||
328 | #s = filename + '(' + `lineno` + ')' |
|
328 | #s = filename + '(' + `lineno` + ')' | |
329 | filename = self.canonic(frame.f_code.co_filename) |
|
329 | filename = self.canonic(frame.f_code.co_filename) | |
330 | link = tpl_link % filename |
|
330 | link = tpl_link % py3compat.cast_unicode(filename) | |
331 |
|
331 | |||
332 | if frame.f_code.co_name: |
|
332 | if frame.f_code.co_name: | |
333 | func = frame.f_code.co_name |
|
333 | func = frame.f_code.co_name | |
@@ -348,10 +348,10 b' class Pdb(OldPdb):' | |||||
348 | ret.append('> ') |
|
348 | ret.append('> ') | |
349 | else: |
|
349 | else: | |
350 | ret.append(' ') |
|
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 | start = lineno - 1 - context//2 |
|
353 | start = lineno - 1 - context//2 | |
354 | lines = linecache.getlines(filename) |
|
354 | lines = ulinecache.getlines(filename) | |
355 | start = max(start, 0) |
|
355 | start = max(start, 0) | |
356 | start = min(start, len(lines) - context) |
|
356 | start = min(start, len(lines) - context) | |
357 | lines = lines[start : start + context] |
|
357 | lines = lines[start : start + context] | |
@@ -364,7 +364,6 b' class Pdb(OldPdb):' | |||||
364 | ret.append(self.__format_line(linetpl, filename, |
|
364 | ret.append(self.__format_line(linetpl, filename, | |
365 | start + 1 + i, line, |
|
365 | start + 1 + i, line, | |
366 | arrow = show_arrow) ) |
|
366 | arrow = show_arrow) ) | |
367 |
|
||||
368 | return ''.join(ret) |
|
367 | return ''.join(ret) | |
369 |
|
368 | |||
370 | def __format_line(self, tpl_line, filename, lineno, line, arrow = False): |
|
369 | def __format_line(self, tpl_line, filename, lineno, line, arrow = False): | |
@@ -422,8 +421,11 b' class Pdb(OldPdb):' | |||||
422 | tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) |
|
421 | tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) | |
423 | tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal) |
|
422 | tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal) | |
424 | src = [] |
|
423 | src = [] | |
|
424 | if filename == "<string>" and hasattr(self, "_exec_filename"): | |||
|
425 | filename = self._exec_filename | |||
|
426 | ||||
425 | for lineno in range(first, last+1): |
|
427 | for lineno in range(first, last+1): | |
426 | line = linecache.getline(filename, lineno) |
|
428 | line = ulinecache.getline(filename, lineno) | |
427 | if not line: |
|
429 | if not line: | |
428 | break |
|
430 | break | |
429 |
|
431 | |||
@@ -475,23 +477,51 b' class Pdb(OldPdb):' | |||||
475 | do_l = do_list |
|
477 | do_l = do_list | |
476 |
|
478 | |||
477 | def do_pdef(self, arg): |
|
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 | namespaces = [('Locals', self.curframe.f_locals), |
|
483 | namespaces = [('Locals', self.curframe.f_locals), | |
480 | ('Globals', self.curframe.f_globals)] |
|
484 | ('Globals', self.curframe.f_globals)] | |
481 | self.shell.find_line_magic('pdef')(arg, namespaces=namespaces) |
|
485 | self.shell.find_line_magic('pdef')(arg, namespaces=namespaces) | |
482 |
|
486 | |||
483 | def do_pdoc(self, arg): |
|
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 | namespaces = [('Locals', self.curframe.f_locals), |
|
491 | namespaces = [('Locals', self.curframe.f_locals), | |
486 | ('Globals', self.curframe.f_globals)] |
|
492 | ('Globals', self.curframe.f_globals)] | |
487 | self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces) |
|
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 | def do_pinfo(self, arg): |
|
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 | namespaces = [('Locals', self.curframe.f_locals), |
|
522 | namespaces = [('Locals', self.curframe.f_locals), | |
492 | ('Globals', self.curframe.f_globals)] |
|
523 | ('Globals', self.curframe.f_globals)] | |
493 |
self.shell.find_line_magic('p |
|
524 | self.shell.find_line_magic('psource')(arg, namespaces=namespaces) | |
494 | namespaces=namespaces) |
|
|||
495 |
|
525 | |||
496 | def checkline(self, filename, lineno): |
|
526 | def checkline(self, filename, lineno): | |
497 | """Check whether specified line seems to be executable. |
|
527 | """Check whether specified line seems to be executable. |
@@ -25,6 +25,9 b' from __future__ import print_function' | |||||
25 |
|
25 | |||
26 | import __builtin__ |
|
26 | import __builtin__ | |
27 |
|
27 | |||
|
28 | import sys | |||
|
29 | ||||
|
30 | ||||
28 | from IPython.config.configurable import Configurable |
|
31 | from IPython.config.configurable import Configurable | |
29 | from IPython.utils import io |
|
32 | from IPython.utils import io | |
30 | from IPython.utils.traitlets import Instance, List |
|
33 | from IPython.utils.traitlets import Instance, List | |
@@ -266,5 +269,7 b' class DisplayHook(Configurable):' | |||||
266 | self.shell.user_ns.update({'_':None,'__':None, '___':None}) |
|
269 | self.shell.user_ns.update({'_':None,'__':None, '___':None}) | |
267 | import gc |
|
270 | import gc | |
268 | # TODO: Is this really needed? |
|
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 | from urllib import urlretrieve |
|
23 | from urllib import urlretrieve | |
24 | from urlparse import urlparse |
|
24 | from urlparse import urlparse | |
25 |
|
25 | |||
|
26 | from IPython.core.error import UsageError | |||
26 | from IPython.config.configurable import Configurable |
|
27 | from IPython.config.configurable import Configurable | |
27 | from IPython.utils.traitlets import Instance |
|
28 | from IPython.utils.traitlets import Instance | |
|
29 | from IPython.utils.py3compat import PY3 | |||
|
30 | if PY3: | |||
|
31 | from imp import reload | |||
28 |
|
32 | |||
29 | #----------------------------------------------------------------------------- |
|
33 | #----------------------------------------------------------------------------- | |
30 | # Main class |
|
34 | # Main class | |
@@ -44,10 +48,11 b' class ExtensionManager(Configurable):' | |||||
44 | the only argument. You can do anything you want with IPython at |
|
48 | the only argument. You can do anything you want with IPython at | |
45 | that point, including defining new magic and aliases, adding new |
|
49 | that point, including defining new magic and aliases, adding new | |
46 | components, etc. |
|
50 | components, etc. | |
47 |
|
51 | |||
48 | The :func:`load_ipython_extension` will be called again is you |
|
52 | You can also optionaly define an :func:`unload_ipython_extension(ipython)` | |
49 | load or reload the extension again. It is up to the extension |
|
53 | function, which will be called if the user unloads or reloads the extension. | |
50 | author to add code to manage that. |
|
54 | The extension manager will only call :func:`load_ipython_extension` again | |
|
55 | if the extension is reloaded. | |||
51 |
|
56 | |||
52 | You can put your extension modules anywhere you want, as long as |
|
57 | You can put your extension modules anywhere you want, as long as | |
53 | they can be imported by Python's standard import mechanism. However, |
|
58 | they can be imported by Python's standard import mechanism. However, | |
@@ -63,6 +68,7 b' class ExtensionManager(Configurable):' | |||||
63 | self.shell.on_trait_change( |
|
68 | self.shell.on_trait_change( | |
64 | self._on_ipython_dir_changed, 'ipython_dir' |
|
69 | self._on_ipython_dir_changed, 'ipython_dir' | |
65 | ) |
|
70 | ) | |
|
71 | self.loaded = set() | |||
66 |
|
72 | |||
67 | def __del__(self): |
|
73 | def __del__(self): | |
68 | self.shell.on_trait_change( |
|
74 | self.shell.on_trait_change( | |
@@ -75,31 +81,48 b' class ExtensionManager(Configurable):' | |||||
75 |
|
81 | |||
76 | def _on_ipython_dir_changed(self): |
|
82 | def _on_ipython_dir_changed(self): | |
77 | if not os.path.isdir(self.ipython_extension_dir): |
|
83 | if not os.path.isdir(self.ipython_extension_dir): | |
78 | os.makedirs(self.ipython_extension_dir, mode = 0777) |
|
84 | os.makedirs(self.ipython_extension_dir, mode = 0o777) | |
79 |
|
85 | |||
80 | def load_extension(self, module_str): |
|
86 | def load_extension(self, module_str): | |
81 | """Load an IPython extension by its module name. |
|
87 | """Load an IPython extension by its module name. | |
82 |
|
88 | |||
83 | If :func:`load_ipython_extension` returns anything, this function |
|
89 | Returns the string "already loaded" if the extension is already loaded, | |
84 | will return that object. |
|
90 | "no load function" if the module doesn't have a load_ipython_extension | |
|
91 | function, or None if it succeeded. | |||
85 | """ |
|
92 | """ | |
|
93 | if module_str in self.loaded: | |||
|
94 | return "already loaded" | |||
|
95 | ||||
86 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
96 | from IPython.utils.syspathcontext import prepended_to_syspath | |
87 |
|
97 | |||
88 | if module_str not in sys.modules: |
|
98 | if module_str not in sys.modules: | |
89 | with prepended_to_syspath(self.ipython_extension_dir): |
|
99 | with prepended_to_syspath(self.ipython_extension_dir): | |
90 | __import__(module_str) |
|
100 | __import__(module_str) | |
91 | mod = sys.modules[module_str] |
|
101 | mod = sys.modules[module_str] | |
92 |
|
|
102 | if self._call_load_ipython_extension(mod): | |
|
103 | self.loaded.add(module_str) | |||
|
104 | else: | |||
|
105 | return "no load function" | |||
93 |
|
106 | |||
94 | def unload_extension(self, module_str): |
|
107 | def unload_extension(self, module_str): | |
95 | """Unload an IPython extension by its module name. |
|
108 | """Unload an IPython extension by its module name. | |
96 |
|
109 | |||
97 | This function looks up the extension's name in ``sys.modules`` and |
|
110 | This function looks up the extension's name in ``sys.modules`` and | |
98 | simply calls ``mod.unload_ipython_extension(self)``. |
|
111 | simply calls ``mod.unload_ipython_extension(self)``. | |
|
112 | ||||
|
113 | Returns the string "no unload function" if the extension doesn't define | |||
|
114 | a function to unload itself, "not loaded" if the extension isn't loaded, | |||
|
115 | otherwise None. | |||
99 | """ |
|
116 | """ | |
|
117 | if module_str not in self.loaded: | |||
|
118 | return "not loaded" | |||
|
119 | ||||
100 | if module_str in sys.modules: |
|
120 | if module_str in sys.modules: | |
101 | mod = sys.modules[module_str] |
|
121 | mod = sys.modules[module_str] | |
102 | self._call_unload_ipython_extension(mod) |
|
122 | if self._call_unload_ipython_extension(mod): | |
|
123 | self.loaded.discard(module_str) | |||
|
124 | else: | |||
|
125 | return "no unload function" | |||
103 |
|
126 | |||
104 | def reload_extension(self, module_str): |
|
127 | def reload_extension(self, module_str): | |
105 | """Reload an IPython extension by calling reload. |
|
128 | """Reload an IPython extension by calling reload. | |
@@ -111,21 +134,25 b' class ExtensionManager(Configurable):' | |||||
111 | """ |
|
134 | """ | |
112 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
135 | from IPython.utils.syspathcontext import prepended_to_syspath | |
113 |
|
136 | |||
114 | with prepended_to_syspath(self.ipython_extension_dir): |
|
137 | if (module_str in self.loaded) and (module_str in sys.modules): | |
115 | if module_str in sys.modules: |
|
138 | self.unload_extension(module_str) | |
116 |
|
|
139 | mod = sys.modules[module_str] | |
|
140 | with prepended_to_syspath(self.ipython_extension_dir): | |||
117 | reload(mod) |
|
141 | reload(mod) | |
118 |
|
|
142 | if self._call_load_ipython_extension(mod): | |
119 | else: |
|
143 | self.loaded.add(module_str) | |
120 | self.load_extension(module_str) |
|
144 | else: | |
|
145 | self.load_extension(module_str) | |||
121 |
|
146 | |||
122 | def _call_load_ipython_extension(self, mod): |
|
147 | def _call_load_ipython_extension(self, mod): | |
123 | if hasattr(mod, 'load_ipython_extension'): |
|
148 | if hasattr(mod, 'load_ipython_extension'): | |
124 |
|
|
149 | mod.load_ipython_extension(self.shell) | |
|
150 | return True | |||
125 |
|
151 | |||
126 | def _call_unload_ipython_extension(self, mod): |
|
152 | def _call_unload_ipython_extension(self, mod): | |
127 | if hasattr(mod, 'unload_ipython_extension'): |
|
153 | if hasattr(mod, 'unload_ipython_extension'): | |
128 |
|
|
154 | mod.unload_ipython_extension(self.shell) | |
|
155 | return True | |||
129 |
|
156 | |||
130 | def install_extension(self, url, filename=None): |
|
157 | def install_extension(self, url, filename=None): | |
131 | """Download and install an IPython extension. |
|
158 | """Download and install an IPython extension. | |
@@ -138,7 +165,7 b' class ExtensionManager(Configurable):' | |||||
138 | """ |
|
165 | """ | |
139 | # Ensure the extension directory exists |
|
166 | # Ensure the extension directory exists | |
140 | if not os.path.isdir(self.ipython_extension_dir): |
|
167 | if not os.path.isdir(self.ipython_extension_dir): | |
141 | os.makedirs(self.ipython_extension_dir, mode = 0777) |
|
168 | os.makedirs(self.ipython_extension_dir, mode = 0o777) | |
142 |
|
169 | |||
143 | if os.path.isfile(url): |
|
170 | if os.path.isfile(url): | |
144 | src_filename = os.path.basename(url) |
|
171 | src_filename = os.path.basename(url) |
@@ -62,6 +62,37 b' def needs_sqlite(f, self, *a, **kw):' | |||||
62 | return f(self, *a, **kw) |
|
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 | class HistoryAccessor(Configurable): |
|
96 | class HistoryAccessor(Configurable): | |
66 | """Access the history database without adding to it. |
|
97 | """Access the history database without adding to it. | |
67 |
|
98 | |||
@@ -143,25 +174,7 b' class HistoryAccessor(Configurable):' | |||||
143 | warn("IPython History requires SQLite, your history will not be saved\n") |
|
174 | warn("IPython History requires SQLite, your history will not be saved\n") | |
144 | self.enabled = False |
|
175 | self.enabled = False | |
145 |
|
176 | |||
146 | if sqlite3 is not None: |
|
177 | self.init_db() | |
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 |
|
|||
165 |
|
178 | |||
166 | def _get_hist_file_name(self, profile='default'): |
|
179 | def _get_hist_file_name(self, profile='default'): | |
167 | """Find the history file for the given profile name. |
|
180 | """Find the history file for the given profile name. | |
@@ -176,6 +189,7 b' class HistoryAccessor(Configurable):' | |||||
176 | """ |
|
189 | """ | |
177 | return os.path.join(locate_profile(profile), 'history.sqlite') |
|
190 | return os.path.join(locate_profile(profile), 'history.sqlite') | |
178 |
|
191 | |||
|
192 | @catch_corrupt_db | |||
179 | def init_db(self): |
|
193 | def init_db(self): | |
180 | """Connect to the database, and create tables if necessary.""" |
|
194 | """Connect to the database, and create tables if necessary.""" | |
181 | if not self.enabled: |
|
195 | if not self.enabled: | |
@@ -235,6 +249,7 b' class HistoryAccessor(Configurable):' | |||||
235 | return cur |
|
249 | return cur | |
236 |
|
250 | |||
237 | @needs_sqlite |
|
251 | @needs_sqlite | |
|
252 | @catch_corrupt_db | |||
238 | def get_session_info(self, session=0): |
|
253 | def get_session_info(self, session=0): | |
239 | """get info about a session |
|
254 | """get info about a session | |
240 |
|
255 | |||
@@ -262,6 +277,7 b' class HistoryAccessor(Configurable):' | |||||
262 | query = "SELECT * from sessions where session == ?" |
|
277 | query = "SELECT * from sessions where session == ?" | |
263 | return self.db.execute(query, (session,)).fetchone() |
|
278 | return self.db.execute(query, (session,)).fetchone() | |
264 |
|
279 | |||
|
280 | @catch_corrupt_db | |||
265 | def get_tail(self, n=10, raw=True, output=False, include_latest=False): |
|
281 | def get_tail(self, n=10, raw=True, output=False, include_latest=False): | |
266 | """Get the last n lines from the history database. |
|
282 | """Get the last n lines from the history database. | |
267 |
|
283 | |||
@@ -289,8 +305,9 b' class HistoryAccessor(Configurable):' | |||||
289 | return reversed(list(cur)[1:]) |
|
305 | return reversed(list(cur)[1:]) | |
290 | return reversed(list(cur)) |
|
306 | return reversed(list(cur)) | |
291 |
|
307 | |||
|
308 | @catch_corrupt_db | |||
292 | def search(self, pattern="*", raw=True, search_raw=True, |
|
309 | def search(self, pattern="*", raw=True, search_raw=True, | |
293 | output=False): |
|
310 | output=False, n=None): | |
294 | """Search the database using unix glob-style matching (wildcards |
|
311 | """Search the database using unix glob-style matching (wildcards | |
295 | * and ?). |
|
312 | * and ?). | |
296 |
|
313 | |||
@@ -302,6 +319,9 b' class HistoryAccessor(Configurable):' | |||||
302 | If True, search the raw input, otherwise, the parsed input |
|
319 | If True, search the raw input, otherwise, the parsed input | |
303 | raw, output : bool |
|
320 | raw, output : bool | |
304 | See :meth:`get_range` |
|
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 | Returns |
|
326 | Returns | |
307 | ------- |
|
327 | ------- | |
@@ -311,9 +331,17 b' class HistoryAccessor(Configurable):' | |||||
311 | if output: |
|
331 | if output: | |
312 | tosearch = "history." + tosearch |
|
332 | tosearch = "history." + tosearch | |
313 | self.writeout_cache() |
|
333 | self.writeout_cache() | |
314 |
|
|
334 | sqlform = "WHERE %s GLOB ?" % tosearch | |
315 | raw=raw, output=output) |
|
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 | def get_range(self, session, start=1, stop=None, raw=True,output=False): |
|
345 | def get_range(self, session, start=1, stop=None, raw=True,output=False): | |
318 | """Retrieve input by session. |
|
346 | """Retrieve input by session. | |
319 |
|
347 | |||
@@ -347,7 +375,7 b' class HistoryAccessor(Configurable):' | |||||
347 | lineclause = "line>=?" |
|
375 | lineclause = "line>=?" | |
348 | params = (session, start) |
|
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 | params, raw=raw, output=output) |
|
379 | params, raw=raw, output=output) | |
352 |
|
380 | |||
353 | def get_range_by_str(self, rangestr, raw=True, output=False): |
|
381 | def get_range_by_str(self, rangestr, raw=True, output=False): |
@@ -29,17 +29,11 b' import runpy' | |||||
29 | import sys |
|
29 | import sys | |
30 | import tempfile |
|
30 | import tempfile | |
31 | import types |
|
31 | import types | |
32 |
|
32 | import urllib | ||
33 | # We need to use nested to support python 2.6, once we move to >=2.7, we can |
|
33 | from io import open as io_open | |
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 |
|
|||
39 |
|
34 | |||
40 | from IPython.config.configurable import SingletonConfigurable |
|
35 | from IPython.config.configurable import SingletonConfigurable | |
41 | from IPython.core import debugger, oinspect |
|
36 | from IPython.core import debugger, oinspect | |
42 | from IPython.core import history as ipcorehist |
|
|||
43 | from IPython.core import magic |
|
37 | from IPython.core import magic | |
44 | from IPython.core import page |
|
38 | from IPython.core import page | |
45 | from IPython.core import prefilter |
|
39 | from IPython.core import prefilter | |
@@ -61,7 +55,6 b' from IPython.core.inputsplitter import IPythonInputSplitter, ESC_MAGIC, ESC_MAGI' | |||||
61 | from IPython.core.logger import Logger |
|
55 | from IPython.core.logger import Logger | |
62 | from IPython.core.macro import Macro |
|
56 | from IPython.core.macro import Macro | |
63 | from IPython.core.payload import PayloadManager |
|
57 | from IPython.core.payload import PayloadManager | |
64 | from IPython.core.plugin import PluginManager |
|
|||
65 | from IPython.core.prefilter import PrefilterManager |
|
58 | from IPython.core.prefilter import PrefilterManager | |
66 | from IPython.core.profiledir import ProfileDir |
|
59 | from IPython.core.profiledir import ProfileDir | |
67 | from IPython.core.pylabtools import pylab_activate |
|
60 | from IPython.core.pylabtools import pylab_activate | |
@@ -392,7 +385,6 b' class InteractiveShell(SingletonConfigurable):' | |||||
392 | builtin_trap = Instance('IPython.core.builtin_trap.BuiltinTrap') |
|
385 | builtin_trap = Instance('IPython.core.builtin_trap.BuiltinTrap') | |
393 | display_trap = Instance('IPython.core.display_trap.DisplayTrap') |
|
386 | display_trap = Instance('IPython.core.display_trap.DisplayTrap') | |
394 | extension_manager = Instance('IPython.core.extensions.ExtensionManager') |
|
387 | extension_manager = Instance('IPython.core.extensions.ExtensionManager') | |
395 | plugin_manager = Instance('IPython.core.plugin.PluginManager') |
|
|||
396 | payload_manager = Instance('IPython.core.payload.PayloadManager') |
|
388 | payload_manager = Instance('IPython.core.payload.PayloadManager') | |
397 | history_manager = Instance('IPython.core.history.HistoryManager') |
|
389 | history_manager = Instance('IPython.core.history.HistoryManager') | |
398 | magics_manager = Instance('IPython.core.magic.MagicsManager') |
|
390 | magics_manager = Instance('IPython.core.magic.MagicsManager') | |
@@ -492,7 +484,6 b' class InteractiveShell(SingletonConfigurable):' | |||||
492 | self.init_logstart() |
|
484 | self.init_logstart() | |
493 | self.init_pdb() |
|
485 | self.init_pdb() | |
494 | self.init_extension_manager() |
|
486 | self.init_extension_manager() | |
495 | self.init_plugin_manager() |
|
|||
496 | self.init_payload() |
|
487 | self.init_payload() | |
497 | self.hooks.late_startup_hook() |
|
488 | self.hooks.late_startup_hook() | |
498 | atexit.register(self.atexit_operations) |
|
489 | atexit.register(self.atexit_operations) | |
@@ -507,7 +498,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
507 |
|
498 | |||
508 | def _ipython_dir_changed(self, name, new): |
|
499 | def _ipython_dir_changed(self, name, new): | |
509 | if not os.path.isdir(new): |
|
500 | if not os.path.isdir(new): | |
510 | os.makedirs(new, mode = 0777) |
|
501 | os.makedirs(new, mode = 0o777) | |
511 |
|
502 | |||
512 | def set_autoindent(self,value=None): |
|
503 | def set_autoindent(self,value=None): | |
513 | """Set the autoindent flag, checking for readline support. |
|
504 | """Set the autoindent flag, checking for readline support. | |
@@ -638,7 +629,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
638 | # override sys.stdout and sys.stderr themselves, you need to do that |
|
629 | # override sys.stdout and sys.stderr themselves, you need to do that | |
639 | # *before* instantiating this class, because io holds onto |
|
630 | # *before* instantiating this class, because io holds onto | |
640 | # references to the underlying streams. |
|
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 | io.stdout = io.stderr = io.IOStream(self.readline._outputfile) |
|
633 | io.stdout = io.stderr = io.IOStream(self.readline._outputfile) | |
643 | else: |
|
634 | else: | |
644 | io.stdout = io.IOStream(sys.stdout) |
|
635 | io.stdout = io.IOStream(sys.stdout) | |
@@ -1722,7 +1713,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
1722 | self.write_err('No traceback available to show.\n') |
|
1713 | self.write_err('No traceback available to show.\n') | |
1723 | return |
|
1714 | return | |
1724 |
|
1715 | |||
1725 |
if etype |
|
1716 | if issubclass(etype, SyntaxError): | |
1726 | # Though this won't be called by syntax errors in the input |
|
1717 | # Though this won't be called by syntax errors in the input | |
1727 | # line, there may be SyntaxError cases with imported code. |
|
1718 | # line, there may be SyntaxError cases with imported code. | |
1728 | self.showsyntaxerror(filename) |
|
1719 | self.showsyntaxerror(filename) | |
@@ -1775,7 +1766,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
1775 | """ |
|
1766 | """ | |
1776 | etype, value, last_traceback = self._get_exc_info() |
|
1767 | etype, value, last_traceback = self._get_exc_info() | |
1777 |
|
1768 | |||
1778 |
if filename and etype |
|
1769 | if filename and issubclass(etype, SyntaxError): | |
1779 | try: |
|
1770 | try: | |
1780 | value.filename = filename |
|
1771 | value.filename = filename | |
1781 | except: |
|
1772 | except: | |
@@ -2288,18 +2279,13 b' class InteractiveShell(SingletonConfigurable):' | |||||
2288 | self.ns_table['alias'] = self.alias_manager.alias_table, |
|
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 | def init_extension_manager(self): |
|
2285 | def init_extension_manager(self): | |
2295 | self.extension_manager = ExtensionManager(shell=self, config=self.config) |
|
2286 | self.extension_manager = ExtensionManager(shell=self, config=self.config) | |
2296 | self.configurables.append(self.extension_manager) |
|
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 | # Things related to payloads |
|
2290 | # Things related to payloads | |
2305 | #------------------------------------------------------------------------- |
|
2291 | #------------------------------------------------------------------------- | |
@@ -2461,9 +2447,6 b' class InteractiveShell(SingletonConfigurable):' | |||||
2461 | dname = os.path.dirname(fname) |
|
2447 | dname = os.path.dirname(fname) | |
2462 |
|
2448 | |||
2463 | with prepended_to_syspath(dname): |
|
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 | try: |
|
2450 | try: | |
2468 | py3compat.execfile(fname,*where) |
|
2451 | py3compat.execfile(fname,*where) | |
2469 | except SystemExit as status: |
|
2452 | except SystemExit as status: | |
@@ -2484,8 +2467,6 b' class InteractiveShell(SingletonConfigurable):' | |||||
2484 | if kw['raise_exceptions']: |
|
2467 | if kw['raise_exceptions']: | |
2485 | raise |
|
2468 | raise | |
2486 | self.showtraceback() |
|
2469 | self.showtraceback() | |
2487 | finally: |
|
|||
2488 | self.user_ns['__file__'] = save_fname |
|
|||
2489 |
|
2470 | |||
2490 | def safe_execfile_ipy(self, fname): |
|
2471 | def safe_execfile_ipy(self, fname): | |
2491 | """Like safe_execfile, but for .ipy files with IPython syntax. |
|
2472 | """Like safe_execfile, but for .ipy files with IPython syntax. | |
@@ -2512,9 +2493,6 b' class InteractiveShell(SingletonConfigurable):' | |||||
2512 | dname = os.path.dirname(fname) |
|
2493 | dname = os.path.dirname(fname) | |
2513 |
|
2494 | |||
2514 | with prepended_to_syspath(dname): |
|
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 | try: |
|
2496 | try: | |
2519 | with open(fname) as thefile: |
|
2497 | with open(fname) as thefile: | |
2520 | # self.run_cell currently captures all exceptions |
|
2498 | # self.run_cell currently captures all exceptions | |
@@ -2525,8 +2503,6 b' class InteractiveShell(SingletonConfigurable):' | |||||
2525 | except: |
|
2503 | except: | |
2526 | self.showtraceback() |
|
2504 | self.showtraceback() | |
2527 | warn('Unknown failure executing file: <%s>' % fname) |
|
2505 | warn('Unknown failure executing file: <%s>' % fname) | |
2528 | finally: |
|
|||
2529 | self.user_ns['__file__'] = save_fname |
|
|||
2530 |
|
2506 | |||
2531 | def safe_run_module(self, mod_name, where): |
|
2507 | def safe_run_module(self, mod_name, where): | |
2532 | """A safe version of runpy.run_module(). |
|
2508 | """A safe version of runpy.run_module(). | |
@@ -2914,7 +2890,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
2914 | lines = self.history_manager.get_range_by_str(range_str, raw=raw) |
|
2890 | lines = self.history_manager.get_range_by_str(range_str, raw=raw) | |
2915 | return "\n".join(x for _, _, x in lines) |
|
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 | """Get a code string from history, file, url, or a string or macro. |
|
2894 | """Get a code string from history, file, url, or a string or macro. | |
2919 |
|
2895 | |||
2920 | This is mainly used by magic functions. |
|
2896 | This is mainly used by magic functions. | |
@@ -2951,7 +2927,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
2951 | utarget = unquote_filename(target) |
|
2927 | utarget = unquote_filename(target) | |
2952 | try: |
|
2928 | try: | |
2953 | if utarget.startswith(('http://', 'https://')): |
|
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 | except UnicodeDecodeError: |
|
2931 | except UnicodeDecodeError: | |
2956 | if not py_only : |
|
2932 | if not py_only : | |
2957 | response = urllib.urlopen(target) |
|
2933 | response = urllib.urlopen(target) | |
@@ -2967,7 +2943,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
2967 | for tgt in potential_target : |
|
2943 | for tgt in potential_target : | |
2968 | if os.path.isfile(tgt): # Read file |
|
2944 | if os.path.isfile(tgt): # Read file | |
2969 | try : |
|
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 | except UnicodeDecodeError : |
|
2947 | except UnicodeDecodeError : | |
2972 | if not py_only : |
|
2948 | if not py_only : | |
2973 | with io_open(tgt,'r', encoding='latin1') as f : |
|
2949 | with io_open(tgt,'r', encoding='latin1') as f : |
@@ -66,7 +66,6 b' class MagicArgumentParser(argparse.ArgumentParser):' | |||||
66 | usage=None, |
|
66 | usage=None, | |
67 | description=None, |
|
67 | description=None, | |
68 | epilog=None, |
|
68 | epilog=None, | |
69 | version=None, |
|
|||
70 | parents=None, |
|
69 | parents=None, | |
71 | formatter_class=MagicHelpFormatter, |
|
70 | formatter_class=MagicHelpFormatter, | |
72 | prefix_chars='-', |
|
71 | prefix_chars='-', | |
@@ -76,7 +75,7 b' class MagicArgumentParser(argparse.ArgumentParser):' | |||||
76 | if parents is None: |
|
75 | if parents is None: | |
77 | parents = [] |
|
76 | parents = [] | |
78 | super(MagicArgumentParser, self).__init__(prog=prog, usage=usage, |
|
77 | super(MagicArgumentParser, self).__init__(prog=prog, usage=usage, | |
79 |
description=description, epilog=epilog, |
|
78 | description=description, epilog=epilog, | |
80 | parents=parents, formatter_class=formatter_class, |
|
79 | parents=parents, formatter_class=formatter_class, | |
81 | prefix_chars=prefix_chars, argument_default=argument_default, |
|
80 | prefix_chars=prefix_chars, argument_default=argument_default, | |
82 | conflict_handler=conflict_handler, add_help=add_help) |
|
81 | conflict_handler=conflict_handler, add_help=add_help) | |
@@ -174,11 +173,17 b' class magic_arguments(ArgDecorator):' | |||||
174 | return func |
|
173 | return func | |
175 |
|
174 | |||
176 |
|
175 | |||
177 |
class |
|
176 | class ArgMethodWrapper(ArgDecorator): | |
178 | """ Store arguments and keywords to pass to add_argument(). |
|
177 | ||
|
178 | """ | |||
|
179 | Base class to define a wrapper for ArgumentParser method. | |||
|
180 | ||||
|
181 | Child class must define either `_method_name` or `add_to_parser`. | |||
179 |
|
182 | |||
180 | Instances also serve to decorate command methods. |
|
|||
181 | """ |
|
183 | """ | |
|
184 | ||||
|
185 | _method_name = None | |||
|
186 | ||||
182 | def __init__(self, *args, **kwds): |
|
187 | def __init__(self, *args, **kwds): | |
183 | self.args = args |
|
188 | self.args = args | |
184 | self.kwds = kwds |
|
189 | self.kwds = kwds | |
@@ -188,18 +193,31 b' class argument(ArgDecorator):' | |||||
188 | """ |
|
193 | """ | |
189 | if group is not None: |
|
194 | if group is not None: | |
190 | parser = group |
|
195 | parser = group | |
191 |
parser. |
|
196 | getattr(parser, self._method_name)(*self.args, **self.kwds) | |
192 | return None |
|
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 | """ Store arguments and keywords to pass to add_argument_group(). |
|
217 | """ Store arguments and keywords to pass to add_argument_group(). | |
197 |
|
218 | |||
198 | Instances also serve to decorate command methods. |
|
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 | def add_to_parser(self, parser, group): |
|
222 | def add_to_parser(self, parser, group): | |
205 | """ Add this object's information to the parser. |
|
223 | """ Add this object's information to the parser. |
@@ -313,7 +313,8 b' Currently the magic system has the following functions:""",' | |||||
313 | import IPython.utils.rlineimpl as readline |
|
313 | import IPython.utils.rlineimpl as readline | |
314 |
|
314 | |||
315 | if not shell.colors_force and \ |
|
315 | if not shell.colors_force and \ | |
316 |
not readline.have_readline and |
|
316 | not readline.have_readline and \ | |
|
317 | (sys.platform == "win32" or sys.platform == "cli"): | |||
317 | msg = """\ |
|
318 | msg = """\ | |
318 | Proper color support under MS Windows requires the pyreadline library. |
|
319 | Proper color support under MS Windows requires the pyreadline library. | |
319 | You can find it at: |
|
320 | You can find it at: |
@@ -44,10 +44,11 b' from IPython.utils import py3compat' | |||||
44 | from IPython.utils.io import capture_output |
|
44 | from IPython.utils.io import capture_output | |
45 | from IPython.utils.ipstruct import Struct |
|
45 | from IPython.utils.ipstruct import Struct | |
46 | from IPython.utils.module_paths import find_mod |
|
46 | from IPython.utils.module_paths import find_mod | |
47 | from IPython.utils.path import get_py_filename, unquote_filename |
|
47 | from IPython.utils.path import get_py_filename, unquote_filename, shellglob | |
48 | from IPython.utils.timing import clock, clock2 |
|
48 | from IPython.utils.timing import clock, clock2 | |
49 | from IPython.utils.warn import warn, error |
|
49 | from IPython.utils.warn import warn, error | |
50 |
|
50 | |||
|
51 | ||||
51 | #----------------------------------------------------------------------------- |
|
52 | #----------------------------------------------------------------------------- | |
52 | # Magic implementation classes |
|
53 | # Magic implementation classes | |
53 | #----------------------------------------------------------------------------- |
|
54 | #----------------------------------------------------------------------------- | |
@@ -324,7 +325,7 b' python-profiler package from non-free.""")' | |||||
324 | """Run the named file inside IPython as a program. |
|
325 | """Run the named file inside IPython as a program. | |
325 |
|
326 | |||
326 | Usage:\\ |
|
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 | Parameters after the filename are passed as command-line arguments to |
|
330 | Parameters after the filename are passed as command-line arguments to | |
330 | the program (put in sys.argv). Then, control returns to IPython's |
|
331 | the program (put in sys.argv). Then, control returns to IPython's | |
@@ -345,6 +346,13 b' python-profiler package from non-free.""")' | |||||
345 | and sys.argv). This allows for very convenient loading of code for |
|
346 | and sys.argv). This allows for very convenient loading of code for | |
346 | interactive work, while giving each program a 'clean sheet' to run in. |
|
347 | interactive work, while giving each program a 'clean sheet' to run in. | |
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 | Options: |
|
356 | Options: | |
349 |
|
357 | |||
350 | -n: __name__ is NOT set to '__main__', but to the running file's name |
|
358 | -n: __name__ is NOT set to '__main__', but to the running file's name | |
@@ -439,10 +447,13 b' python-profiler package from non-free.""")' | |||||
439 |
|
447 | |||
440 | will run the example module. |
|
448 | will run the example module. | |
441 |
|
449 | |||
|
450 | -G: disable shell-like glob expansion of arguments. | |||
|
451 | ||||
442 | """ |
|
452 | """ | |
443 |
|
453 | |||
444 | # get arguments and set sys.argv for program to be run. |
|
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 | mode='list', list_all=1) |
|
457 | mode='list', list_all=1) | |
447 | if "m" in opts: |
|
458 | if "m" in opts: | |
448 | modulename = opts["m"][0] |
|
459 | modulename = opts["m"][0] | |
@@ -476,8 +487,11 b' python-profiler package from non-free.""")' | |||||
476 | # were run from a system shell. |
|
487 | # were run from a system shell. | |
477 | save_argv = sys.argv # save it for later restoring |
|
488 | save_argv = sys.argv # save it for later restoring | |
478 |
|
489 | |||
479 | # simulate shell expansion on arguments, at least tilde expansion |
|
490 | if 'G' in opts: | |
480 | args = [ os.path.expanduser(a) for a in arg_lst[1:] ] |
|
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 | sys.argv = [filename] + args # put in the proper filename |
|
496 | sys.argv = [filename] + args # put in the proper filename | |
483 | # protect sys.argv from potential unicode strings on Python 2: |
|
497 | # protect sys.argv from potential unicode strings on Python 2: | |
@@ -549,11 +563,18 b' python-profiler package from non-free.""")' | |||||
549 | return |
|
563 | return | |
550 | # if we find a good linenumber, set the breakpoint |
|
564 | # if we find a good linenumber, set the breakpoint | |
551 | deb.do_break('%s:%s' % (filename, bp)) |
|
565 | deb.do_break('%s:%s' % (filename, bp)) | |
|
566 | ||||
|
567 | # Mimic Pdb._runscript(...) | |||
|
568 | deb._wait_for_mainpyfile = True | |||
|
569 | deb.mainpyfile = deb.canonic(filename) | |||
|
570 | ||||
552 | # Start file run |
|
571 | # Start file run | |
553 | print "NOTE: Enter 'c' at the", |
|
572 | print "NOTE: Enter 'c' at the", | |
554 | print "%s prompt to start your script." % deb.prompt |
|
573 | print "%s prompt to start your script." % deb.prompt | |
555 | ns = {'execfile': py3compat.execfile, 'prog_ns': prog_ns} |
|
574 | ns = {'execfile': py3compat.execfile, 'prog_ns': prog_ns} | |
556 | try: |
|
575 | try: | |
|
576 | #save filename so it can be used by methods on the deb object | |||
|
577 | deb._exec_filename = filename | |||
557 | deb.run('execfile("%s", prog_ns)' % filename, ns) |
|
578 | deb.run('execfile("%s", prog_ns)' % filename, ns) | |
558 |
|
579 | |||
559 | except: |
|
580 | except: |
@@ -59,14 +59,30 b' class ExtensionMagics(Magics):' | |||||
59 | """Load an IPython extension by its module name.""" |
|
59 | """Load an IPython extension by its module name.""" | |
60 | if not module_str: |
|
60 | if not module_str: | |
61 | raise UsageError('Missing module name.') |
|
61 | raise UsageError('Missing module name.') | |
62 |
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 | @line_magic |
|
70 | @line_magic | |
65 | def unload_ext(self, module_str): |
|
71 | def unload_ext(self, module_str): | |
66 |
"""Unload an IPython extension by its module name. |
|
72 | """Unload an IPython extension by its module name. | |
|
73 | ||||
|
74 | Not all extensions can be unloaded, only those which define an | |||
|
75 | ``unload_ipython_extension`` function. | |||
|
76 | """ | |||
67 | if not module_str: |
|
77 | if not module_str: | |
68 | raise UsageError('Missing module name.') |
|
78 | raise UsageError('Missing module name.') | |
69 | self.shell.extension_manager.unload_extension(module_str) |
|
79 | ||
|
80 | res = self.shell.extension_manager.unload_extension(module_str) | |||
|
81 | ||||
|
82 | if res == 'no unload function': | |||
|
83 | print "The %s extension doesn't define how to unload it." % module_str | |||
|
84 | elif res == "not loaded": | |||
|
85 | print "The %s extension is not loaded." % module_str | |||
70 |
|
86 | |||
71 | @line_magic |
|
87 | @line_magic | |
72 | def reload_ext(self, module_str): |
|
88 | def reload_ext(self, module_str): |
@@ -16,10 +16,13 b' from __future__ import print_function' | |||||
16 | # Stdlib |
|
16 | # Stdlib | |
17 | import os |
|
17 | import os | |
18 | from io import open as io_open |
|
18 | from io import open as io_open | |
|
19 | from IPython.external.argparse import Action | |||
19 |
|
20 | |||
20 | # Our own packages |
|
21 | # Our own packages | |
21 | from IPython.core.error import StdinNotImplementedError |
|
22 | from IPython.core.error import StdinNotImplementedError | |
22 | from IPython.core.magic import Magics, magics_class, line_magic |
|
23 | from IPython.core.magic import Magics, magics_class, line_magic | |
|
24 | from IPython.core.magic_arguments import (argument, magic_arguments, | |||
|
25 | parse_argstring) | |||
23 | from IPython.testing.skipdoctest import skip_doctest |
|
26 | from IPython.testing.skipdoctest import skip_doctest | |
24 | from IPython.utils import io |
|
27 | from IPython.utils import io | |
25 |
|
28 | |||
@@ -27,16 +30,71 b' from IPython.utils import io' | |||||
27 | # Magics class implementation |
|
30 | # Magics class implementation | |
28 | #----------------------------------------------------------------------------- |
|
31 | #----------------------------------------------------------------------------- | |
29 |
|
32 | |||
|
33 | ||||
|
34 | _unspecified = object() | |||
|
35 | ||||
|
36 | ||||
30 | @magics_class |
|
37 | @magics_class | |
31 | class HistoryMagics(Magics): |
|
38 | class HistoryMagics(Magics): | |
32 |
|
39 | |||
|
40 | @magic_arguments() | |||
|
41 | @argument( | |||
|
42 | '-n', dest='print_nums', action='store_true', default=False, | |||
|
43 | help=""" | |||
|
44 | print line numbers for each input. | |||
|
45 | This feature is only available if numbered prompts are in use. | |||
|
46 | """) | |||
|
47 | @argument( | |||
|
48 | '-o', dest='get_output', action='store_true', default=False, | |||
|
49 | help="also print outputs for each input.") | |||
|
50 | @argument( | |||
|
51 | '-p', dest='pyprompts', action='store_true', default=False, | |||
|
52 | help=""" | |||
|
53 | print classic '>>>' python prompts before each input. | |||
|
54 | This is useful for making documentation, and in conjunction | |||
|
55 | with -o, for producing doctest-ready output. | |||
|
56 | """) | |||
|
57 | @argument( | |||
|
58 | '-t', dest='raw', action='store_false', default=True, | |||
|
59 | help=""" | |||
|
60 | print the 'translated' history, as IPython understands it. | |||
|
61 | IPython filters your input and converts it all into valid Python | |||
|
62 | source before executing it (things like magics or aliases are turned | |||
|
63 | into function calls, for example). With this option, you'll see the | |||
|
64 | native history instead of the user-entered version: '%%cd /' will be | |||
|
65 | seen as 'get_ipython().magic("%%cd /")' instead of '%%cd /'. | |||
|
66 | """) | |||
|
67 | @argument( | |||
|
68 | '-f', dest='filename', | |||
|
69 | help=""" | |||
|
70 | FILENAME: instead of printing the output to the screen, redirect | |||
|
71 | it to the given file. The file is always overwritten, though *when | |||
|
72 | it can*, IPython asks for confirmation first. In particular, running | |||
|
73 | the command 'history -f FILENAME' from the IPython Notebook | |||
|
74 | interface will replace FILENAME even if it already exists *without* | |||
|
75 | confirmation. | |||
|
76 | """) | |||
|
77 | @argument( | |||
|
78 | '-g', dest='pattern', nargs='*', default=None, | |||
|
79 | help=""" | |||
|
80 | treat the arg as a glob pattern to search for in (full) history. | |||
|
81 | This includes the saved history (almost all commands ever written). | |||
|
82 | The pattern may contain '?' to match one unknown character and '*' | |||
|
83 | to match any number of unknown characters. Use '%%hist -g' to show | |||
|
84 | full saved history (may be very long). | |||
|
85 | """) | |||
|
86 | @argument( | |||
|
87 | '-l', dest='limit', type=int, nargs='?', default=_unspecified, | |||
|
88 | help=""" | |||
|
89 | get the last n lines from all sessions. Specify n as a single | |||
|
90 | arg, or the default is the last 10 lines. | |||
|
91 | """) | |||
|
92 | @argument('range', nargs='*') | |||
33 | @skip_doctest |
|
93 | @skip_doctest | |
34 | @line_magic |
|
94 | @line_magic | |
35 | def history(self, parameter_s = ''): |
|
95 | def history(self, parameter_s = ''): | |
36 | """Print input history (_i<n> variables), with most recent last. |
|
96 | """Print input history (_i<n> variables), with most recent last. | |
37 |
|
97 | |||
38 | %history [-o -p -t -n] [-f filename] [range | -g pattern | -l number] |
|
|||
39 |
|
||||
40 | By default, input history is printed without line numbers so it can be |
|
98 | By default, input history is printed without line numbers so it can be | |
41 | directly pasted into an editor. Use -n to show them. |
|
99 | directly pasted into an editor. Use -n to show them. | |
42 |
|
100 | |||
@@ -52,43 +110,6 b' class HistoryMagics(Magics):' | |||||
52 |
|
110 | |||
53 | The same syntax is used by %macro, %save, %edit, %rerun |
|
111 | The same syntax is used by %macro, %save, %edit, %rerun | |
54 |
|
112 | |||
55 | Options: |
|
|||
56 |
|
||||
57 | -n: print line numbers for each input. |
|
|||
58 | This feature is only available if numbered prompts are in use. |
|
|||
59 |
|
||||
60 | -o: also print outputs for each input. |
|
|||
61 |
|
||||
62 | -p: print classic '>>>' python prompts before each input. This is |
|
|||
63 | useful for making documentation, and in conjunction with -o, for |
|
|||
64 | producing doctest-ready output. |
|
|||
65 |
|
||||
66 | -r: (default) print the 'raw' history, i.e. the actual commands you |
|
|||
67 | typed. |
|
|||
68 |
|
||||
69 | -t: print the 'translated' history, as IPython understands it. |
|
|||
70 | IPython filters your input and converts it all into valid Python |
|
|||
71 | source before executing it (things like magics or aliases are turned |
|
|||
72 | into function calls, for example). With this option, you'll see the |
|
|||
73 | native history instead of the user-entered version: '%cd /' will be |
|
|||
74 | seen as 'get_ipython().magic("%cd /")' instead of '%cd /'. |
|
|||
75 |
|
||||
76 | -g: treat the arg as a pattern to grep for in (full) history. |
|
|||
77 | This includes the saved history (almost all commands ever written). |
|
|||
78 | The pattern may contain '?' to match one unknown character and '*' |
|
|||
79 | to match any number of unknown characters. Use '%hist -g' to show |
|
|||
80 | full saved history (may be very long). |
|
|||
81 |
|
||||
82 | -l: get the last n lines from all sessions. Specify n as a single |
|
|||
83 | arg, or the default is the last 10 lines. |
|
|||
84 |
|
||||
85 | -f FILENAME: instead of printing the output to the screen, redirect |
|
|||
86 | it to the given file. The file is always overwritten, though *when |
|
|||
87 | it can*, IPython asks for confirmation first. In particular, running |
|
|||
88 | the command 'history -f FILENAME' from the IPython Notebook |
|
|||
89 | interface will replace FILENAME even if it already exists *without* |
|
|||
90 | confirmation. |
|
|||
91 |
|
||||
92 | Examples |
|
113 | Examples | |
93 | -------- |
|
114 | -------- | |
94 | :: |
|
115 | :: | |
@@ -100,11 +121,7 b' class HistoryMagics(Magics):' | |||||
100 |
|
121 | |||
101 | """ |
|
122 | """ | |
102 |
|
123 | |||
103 | if not self.shell.displayhook.do_full_cache: |
|
124 | args = parse_argstring(self.history, parameter_s) | |
104 | print('This feature is only available if numbered prompts ' |
|
|||
105 | 'are in use.') |
|
|||
106 | return |
|
|||
107 | opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string') |
|
|||
108 |
|
125 | |||
109 | # For brevity |
|
126 | # For brevity | |
110 | history_manager = self.shell.history_manager |
|
127 | history_manager = self.shell.history_manager | |
@@ -116,9 +133,8 b' class HistoryMagics(Magics):' | |||||
116 | return "%s/%s" % (session, line) |
|
133 | return "%s/%s" % (session, line) | |
117 |
|
134 | |||
118 | # Check if output to specific file was requested. |
|
135 | # Check if output to specific file was requested. | |
119 | try: |
|
136 | outfname = args.filename | |
120 |
|
|
137 | if not outfname: | |
121 | except KeyError: |
|
|||
122 | outfile = io.stdout # default |
|
138 | outfile = io.stdout # default | |
123 | # We don't want to close stdout at the end! |
|
139 | # We don't want to close stdout at the end! | |
124 | close_at_end = False |
|
140 | close_at_end = False | |
@@ -135,27 +151,29 b' class HistoryMagics(Magics):' | |||||
135 | outfile = io_open(outfname, 'w', encoding='utf-8') |
|
151 | outfile = io_open(outfname, 'w', encoding='utf-8') | |
136 | close_at_end = True |
|
152 | close_at_end = True | |
137 |
|
153 | |||
138 |
print_nums = |
|
154 | print_nums = args.print_nums | |
139 |
get_output = |
|
155 | get_output = args.get_output | |
140 |
pyprompts = |
|
156 | pyprompts = args.pyprompts | |
141 | # Raw history is the default |
|
157 | raw = args.raw | |
142 | raw = not('t' in opts) |
|
|||
143 |
|
158 | |||
144 | pattern = None |
|
159 | pattern = None | |
|
160 | limit = None if args.limit is _unspecified else args.limit | |||
145 |
|
161 | |||
146 | if 'g' in opts: # Glob search |
|
162 | if args.pattern is not None: | |
147 | pattern = "*" + args + "*" if args else "*" |
|
163 | if args.pattern: | |
148 | hist = history_manager.search(pattern, raw=raw, output=get_output) |
|
164 | pattern = "*" + " ".join(args.pattern) + "*" | |
|
165 | else: | |||
|
166 | pattern = "*" | |||
|
167 | hist = history_manager.search(pattern, raw=raw, output=get_output, | |||
|
168 | n=limit) | |||
149 | print_nums = True |
|
169 | print_nums = True | |
150 | elif 'l' in opts: # Get 'tail' |
|
170 | elif args.limit is not _unspecified: | |
151 | try: |
|
171 | n = 10 if limit is None else limit | |
152 | n = int(args) |
|
|||
153 | except (ValueError, IndexError): |
|
|||
154 | n = 10 |
|
|||
155 | hist = history_manager.get_tail(n, raw=raw, output=get_output) |
|
172 | hist = history_manager.get_tail(n, raw=raw, output=get_output) | |
156 | else: |
|
173 | else: | |
157 |
if args: |
|
174 | if args.range: # Get history by ranges | |
158 |
hist = history_manager.get_range_by_str(args, |
|
175 | hist = history_manager.get_range_by_str(" ".join(args.range), | |
|
176 | raw, get_output) | |||
159 | else: # Just get history for the current session |
|
177 | else: # Just get history for the current session | |
160 | hist = history_manager.get_range(raw=raw, output=get_output) |
|
178 | hist = history_manager.get_range(raw=raw, output=get_output) | |
161 |
|
179 |
@@ -23,6 +23,7 b' from IPython.core.error import StdinNotImplementedError, UsageError' | |||||
23 | from IPython.core.magic import Magics, magics_class, line_magic |
|
23 | from IPython.core.magic import Magics, magics_class, line_magic | |
24 | from IPython.testing.skipdoctest import skip_doctest |
|
24 | from IPython.testing.skipdoctest import skip_doctest | |
25 | from IPython.utils.encoding import DEFAULT_ENCODING |
|
25 | from IPython.utils.encoding import DEFAULT_ENCODING | |
|
26 | from IPython.utils.openpy import read_py_file | |||
26 | from IPython.utils.path import get_py_filename |
|
27 | from IPython.utils.path import get_py_filename | |
27 |
|
28 | |||
28 | #----------------------------------------------------------------------------- |
|
29 | #----------------------------------------------------------------------------- | |
@@ -68,7 +69,7 b' class NamespaceMagics(Magics):' | |||||
68 | @skip_doctest |
|
69 | @skip_doctest | |
69 | @line_magic |
|
70 | @line_magic | |
70 | def pdef(self, parameter_s='', namespaces=None): |
|
71 | def pdef(self, parameter_s='', namespaces=None): | |
71 |
"""Print the |
|
72 | """Print the call signature for any callable object. | |
72 |
|
73 | |||
73 | If the object is a class, print the constructor information. |
|
74 | If the object is a class, print the constructor information. | |
74 |
|
75 | |||
@@ -97,7 +98,7 b' class NamespaceMagics(Magics):' | |||||
97 | self.shell._inspect('psource',parameter_s, namespaces) |
|
98 | self.shell._inspect('psource',parameter_s, namespaces) | |
98 |
|
99 | |||
99 | @line_magic |
|
100 | @line_magic | |
100 | def pfile(self, parameter_s=''): |
|
101 | def pfile(self, parameter_s='', namespaces=None): | |
101 | """Print (or run through pager) the file where an object is defined. |
|
102 | """Print (or run through pager) the file where an object is defined. | |
102 |
|
103 | |||
103 | The file opens at the line where the object definition begins. IPython |
|
104 | The file opens at the line where the object definition begins. IPython | |
@@ -110,7 +111,7 b' class NamespaceMagics(Magics):' | |||||
110 | viewer.""" |
|
111 | viewer.""" | |
111 |
|
112 | |||
112 | # first interpret argument as an object name |
|
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 | # if not, try the input as a filename |
|
115 | # if not, try the input as a filename | |
115 | if out == 'not found': |
|
116 | if out == 'not found': | |
116 | try: |
|
117 | try: | |
@@ -118,7 +119,7 b' class NamespaceMagics(Magics):' | |||||
118 | except IOError as msg: |
|
119 | except IOError as msg: | |
119 | print msg |
|
120 | print msg | |
120 | return |
|
121 | return | |
121 |
page.page(self.shell. |
|
122 | page.page(self.shell.pycolorize(read_py_file(filename, skip_encoding_cookie=False))) | |
122 |
|
123 | |||
123 | @line_magic |
|
124 | @line_magic | |
124 | def psearch(self, parameter_s=''): |
|
125 | def psearch(self, parameter_s=''): |
@@ -32,6 +32,7 b' from IPython.core.magic import (' | |||||
32 | ) |
|
32 | ) | |
33 | from IPython.testing.skipdoctest import skip_doctest |
|
33 | from IPython.testing.skipdoctest import skip_doctest | |
34 | from IPython.utils.io import file_read, nlprint |
|
34 | from IPython.utils.io import file_read, nlprint | |
|
35 | from IPython.utils.openpy import source_to_unicode | |||
35 | from IPython.utils.path import get_py_filename, unquote_filename |
|
36 | from IPython.utils.path import get_py_filename, unquote_filename | |
36 | from IPython.utils.process import abbrev_cwd |
|
37 | from IPython.utils.process import abbrev_cwd | |
37 | from IPython.utils.terminal import set_term_title |
|
38 | from IPython.utils.terminal import set_term_title | |
@@ -686,12 +687,12 b' class OSMagics(Magics):' | |||||
686 | 'or macro.') |
|
687 | 'or macro.') | |
687 |
|
688 | |||
688 | try : |
|
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 | except (ValueError, IOError): |
|
691 | except (ValueError, IOError): | |
691 | print "Error: no such file, variable, URL, history range or macro" |
|
692 | print "Error: no such file, variable, URL, history range or macro" | |
692 | return |
|
693 | return | |
693 |
|
694 | |||
694 | page.page(self.shell.pycolorize(cont)) |
|
695 | page.page(self.shell.pycolorize(source_to_unicode(cont))) | |
695 |
|
696 | |||
696 | @magic_arguments.magic_arguments() |
|
697 | @magic_arguments.magic_arguments() | |
697 | @magic_arguments.argument( |
|
698 | @magic_arguments.argument( |
@@ -12,6 +12,7 b'' | |||||
12 | #----------------------------------------------------------------------------- |
|
12 | #----------------------------------------------------------------------------- | |
13 |
|
13 | |||
14 | # Stdlib |
|
14 | # Stdlib | |
|
15 | import errno | |||
15 | import os |
|
16 | import os | |
16 | import re |
|
17 | import re | |
17 | import sys |
|
18 | import sys | |
@@ -30,7 +31,7 b' from IPython.core.magic import (' | |||||
30 | from IPython.lib.backgroundjobs import BackgroundJobManager |
|
31 | from IPython.lib.backgroundjobs import BackgroundJobManager | |
31 | from IPython.testing.skipdoctest import skip_doctest |
|
32 | from IPython.testing.skipdoctest import skip_doctest | |
32 | from IPython.utils import py3compat |
|
33 | from IPython.utils import py3compat | |
33 |
from IPython.utils.process import |
|
34 | from IPython.utils.process import arg_split | |
34 | from IPython.utils.traitlets import List, Dict |
|
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 | def _script_magics_default(self): |
|
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 | defaults = [ | |
96 | to_try = [] |
|
|||
97 | if os.name == 'nt': |
|
|||
98 | defaults.append('cmd') |
|
|||
99 | to_try.append('powershell') |
|
|||
100 | to_try.extend([ |
|
|||
101 | 'sh', |
|
97 | 'sh', | |
102 | 'bash', |
|
98 | 'bash', | |
103 | 'perl', |
|
99 | 'perl', | |
104 | 'ruby', |
|
100 | 'ruby', | |
|
101 | 'python', | |||
105 | 'python3', |
|
102 | 'python3', | |
106 | 'pypy', |
|
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 | return defaults |
|
111 | return defaults | |
124 |
|
112 | |||
125 | script_paths = Dict(config=True, |
|
113 | script_paths = Dict(config=True, | |
@@ -197,8 +185,15 b' class ScriptMagics(Magics, Configurable):' | |||||
197 | """ |
|
185 | """ | |
198 | argv = arg_split(line, posix = not sys.platform.startswith('win')) |
|
186 | argv = arg_split(line, posix = not sys.platform.startswith('win')) | |
199 | args, cmd = self.shebang.parser.parse_known_args(argv) |
|
187 | args, cmd = self.shebang.parser.parse_known_args(argv) | |
200 |
|
188 | |||
201 | p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE) |
|
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 | cell = cell.encode('utf8', 'replace') |
|
198 | cell = cell.encode('utf8', 'replace') | |
204 | if args.bg: |
|
199 | if args.bg: |
@@ -24,6 +24,8 b' import linecache' | |||||
24 | import os |
|
24 | import os | |
25 | import sys |
|
25 | import sys | |
26 | import types |
|
26 | import types | |
|
27 | import io as stdlib_io | |||
|
28 | ||||
27 | from collections import namedtuple |
|
29 | from collections import namedtuple | |
28 | try: |
|
30 | try: | |
29 | from itertools import izip_longest |
|
31 | from itertools import izip_longest | |
@@ -35,10 +37,12 b' from IPython.core import page' | |||||
35 | from IPython.testing.skipdoctest import skip_doctest_py3 |
|
37 | from IPython.testing.skipdoctest import skip_doctest_py3 | |
36 | from IPython.utils import PyColorize |
|
38 | from IPython.utils import PyColorize | |
37 | from IPython.utils import io |
|
39 | from IPython.utils import io | |
|
40 | from IPython.utils import openpy | |||
38 | from IPython.utils import py3compat |
|
41 | from IPython.utils import py3compat | |
39 | from IPython.utils.text import indent |
|
42 | from IPython.utils.text import indent | |
40 | from IPython.utils.wildcard import list_namespace |
|
43 | from IPython.utils.wildcard import list_namespace | |
41 | from IPython.utils.coloransi import * |
|
44 | from IPython.utils.coloransi import * | |
|
45 | from IPython.utils.py3compat import cast_unicode | |||
42 |
|
46 | |||
43 | #**************************************************************************** |
|
47 | #**************************************************************************** | |
44 | # Builtin color schemes |
|
48 | # Builtin color schemes | |
@@ -90,6 +94,29 b' def object_info(**kw):' | |||||
90 | return infodict |
|
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 | def getdoc(obj): |
|
120 | def getdoc(obj): | |
94 | """Stable wrapper around inspect.getdoc. |
|
121 | """Stable wrapper around inspect.getdoc. | |
95 |
|
122 | |||
@@ -109,10 +136,13 b' def getdoc(obj):' | |||||
109 | return inspect.cleandoc(ds) |
|
136 | return inspect.cleandoc(ds) | |
110 |
|
137 | |||
111 | try: |
|
138 | try: | |
112 |
|
|
139 | docstr = inspect.getdoc(obj) | |
|
140 | encoding = get_encoding(obj) | |||
|
141 | return py3compat.cast_unicode(docstr, encoding=encoding) | |||
113 | except Exception: |
|
142 | except Exception: | |
114 | # Harden against an inspect failure, which can occur with |
|
143 | # Harden against an inspect failure, which can occur with | |
115 | # SWIG-wrapped extensions. |
|
144 | # SWIG-wrapped extensions. | |
|
145 | raise | |||
116 | return None |
|
146 | return None | |
117 |
|
147 | |||
118 |
|
148 | |||
@@ -143,7 +173,8 b' def getsource(obj,is_binary=False):' | |||||
143 | except TypeError: |
|
173 | except TypeError: | |
144 | if hasattr(obj,'__class__'): |
|
174 | if hasattr(obj,'__class__'): | |
145 | src = inspect.getsource(obj.__class__) |
|
175 | src = inspect.getsource(obj.__class__) | |
146 | return src |
|
176 | encoding = get_encoding(obj) | |
|
177 | return cast_unicode(src, encoding=encoding) | |||
147 |
|
178 | |||
148 | def getargspec(obj): |
|
179 | def getargspec(obj): | |
149 | """Get the names and default values of a function's arguments. |
|
180 | """Get the names and default values of a function's arguments. | |
@@ -313,15 +344,14 b' class Inspector:' | |||||
313 | self.set_active_scheme(scheme) |
|
344 | self.set_active_scheme(scheme) | |
314 |
|
345 | |||
315 | def _getdef(self,obj,oname=''): |
|
346 | def _getdef(self,obj,oname=''): | |
316 |
"""Return the |
|
347 | """Return the call signature for any callable object. | |
317 |
|
348 | |||
318 | If any exception is generated, None is returned instead and the |
|
349 | If any exception is generated, None is returned instead and the | |
319 | exception is suppressed.""" |
|
350 | exception is suppressed.""" | |
320 |
|
351 | |||
321 | try: |
|
352 | try: | |
322 | # We need a plain string here, NOT unicode! |
|
|||
323 | hdef = oname + inspect.formatargspec(*getargspec(obj)) |
|
353 | hdef = oname + inspect.formatargspec(*getargspec(obj)) | |
324 |
return |
|
354 | return cast_unicode(hdef) | |
325 | except: |
|
355 | except: | |
326 | return None |
|
356 | return None | |
327 |
|
357 | |||
@@ -343,7 +373,7 b' class Inspector:' | |||||
343 | print() |
|
373 | print() | |
344 |
|
374 | |||
345 | def pdef(self, obj, oname=''): |
|
375 | def pdef(self, obj, oname=''): | |
346 |
"""Print the |
|
376 | """Print the call signature for any callable object. | |
347 |
|
377 | |||
348 | If the object is a class, print the constructor information.""" |
|
378 | If the object is a class, print the constructor information.""" | |
349 |
|
379 | |||
@@ -435,7 +465,7 b' class Inspector:' | |||||
435 | except: |
|
465 | except: | |
436 | self.noinfo('source',oname) |
|
466 | self.noinfo('source',oname) | |
437 | else: |
|
467 | else: | |
438 |
page.page(self.format( |
|
468 | page.page(self.format(src)) | |
439 |
|
469 | |||
440 | def pfile(self, obj, oname=''): |
|
470 | def pfile(self, obj, oname=''): | |
441 | """Show the whole file where an object was defined.""" |
|
471 | """Show the whole file where an object was defined.""" | |
@@ -457,7 +487,7 b' class Inspector:' | |||||
457 | # Print only text files, not extension binaries. Note that |
|
487 | # Print only text files, not extension binaries. Note that | |
458 | # getsourcelines returns lineno with 1-offset and page() uses |
|
488 | # getsourcelines returns lineno with 1-offset and page() uses | |
459 | # 0-offset, so we must adjust. |
|
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 | def _format_fields(self, fields, title_width=12): |
|
492 | def _format_fields(self, fields, title_width=12): | |
463 | """Formats a list of fields for display. |
|
493 | """Formats a list of fields for display. | |
@@ -476,7 +506,7 b' class Inspector:' | |||||
476 | title = header(title + ":") + "\n" |
|
506 | title = header(title + ":") + "\n" | |
477 | else: |
|
507 | else: | |
478 | title = header((title+":").ljust(title_width)) |
|
508 | title = header((title+":").ljust(title_width)) | |
479 | out.append(title + content) |
|
509 | out.append(cast_unicode(title) + cast_unicode(content)) | |
480 | return "\n".join(out) |
|
510 | return "\n".join(out) | |
481 |
|
511 | |||
482 | # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict) |
|
512 | # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict) | |
@@ -536,7 +566,8 b' class Inspector:' | |||||
536 | # Source or docstring, depending on detail level and whether |
|
566 | # Source or docstring, depending on detail level and whether | |
537 | # source found. |
|
567 | # source found. | |
538 | if detail_level > 0 and info['source'] is not None: |
|
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 | elif info['docstring'] is not None: |
|
571 | elif info['docstring'] is not None: | |
541 | displayfields.append(("Docstring", info["docstring"])) |
|
572 | displayfields.append(("Docstring", info["docstring"])) | |
542 |
|
573 |
@@ -110,10 +110,10 b' class ProfileDir(LoggingConfigurable):' | |||||
110 |
|
110 | |||
111 | def check_security_dir(self): |
|
111 | def check_security_dir(self): | |
112 | if not os.path.isdir(self.security_dir): |
|
112 | if not os.path.isdir(self.security_dir): | |
113 | os.mkdir(self.security_dir, 0700) |
|
113 | os.mkdir(self.security_dir, 0o700) | |
114 | else: |
|
114 | else: | |
115 | try: |
|
115 | try: | |
116 | os.chmod(self.security_dir, 0700) |
|
116 | os.chmod(self.security_dir, 0o700) | |
117 | except OSError: |
|
117 | except OSError: | |
118 | self.log.warn("Could not set security dir permissions to private.") |
|
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 | def check_pid_dir(self): |
|
123 | def check_pid_dir(self): | |
124 | if not os.path.isdir(self.pid_dir): |
|
124 | if not os.path.isdir(self.pid_dir): | |
125 | os.mkdir(self.pid_dir, 0700) |
|
125 | os.mkdir(self.pid_dir, 0o700) | |
126 | else: |
|
126 | else: | |
127 | try: |
|
127 | try: | |
128 | os.chmod(self.pid_dir, 0700) |
|
128 | os.chmod(self.pid_dir, 0o700) | |
129 | except OSError: |
|
129 | except OSError: | |
130 | self.log.warn("Could not set pid dir permissions to private.") |
|
130 | self.log.warn("Could not set pid dir permissions to private.") | |
131 |
|
131 |
@@ -21,7 +21,7 b" name = 'ipython'" | |||||
21 | # version |
|
21 | # version | |
22 | _version_major = 0 |
|
22 | _version_major = 0 | |
23 | _version_minor = 14 |
|
23 | _version_minor = 14 | |
24 |
_version_micro = |
|
24 | _version_micro = 0 # use 0 for first of series, number for 1 and above | |
25 | _version_extra = 'dev' |
|
25 | _version_extra = 'dev' | |
26 | #_version_extra = 'rc1' |
|
26 | #_version_extra = 'rc1' | |
27 | # _version_extra = '' # Uncomment this for full releases |
|
27 | # _version_extra = '' # Uncomment this for full releases | |
@@ -36,6 +36,7 b' if _version_extra:' | |||||
36 | __version__ = '.'.join(map(str, _ver)) |
|
36 | __version__ = '.'.join(map(str, _ver)) | |
37 |
|
37 | |||
38 | version = __version__ # backwards compatibility name |
|
38 | version = __version__ # backwards compatibility name | |
|
39 | version_info = (_version_major, _version_minor, _version_micro, _version_extra) | |||
39 |
|
40 | |||
40 | description = "IPython: Productive Interactive Computing" |
|
41 | description = "IPython: Productive Interactive Computing" | |
41 |
|
42 |
@@ -277,12 +277,20 b' class InteractiveShellApp(Configurable):' | |||||
277 | sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ] |
|
277 | sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ] | |
278 | try: |
|
278 | try: | |
279 | if os.path.isfile(full_filename): |
|
279 | if os.path.isfile(full_filename): | |
280 | self.log.info("Running file in user namespace: %s" % full_filename) |
|
|||
281 | if full_filename.endswith('.ipy'): |
|
280 | if full_filename.endswith('.ipy'): | |
|
281 | self.log.info("Running file in user namespace: %s" % | |||
|
282 | full_filename) | |||
282 | self.shell.safe_execfile_ipy(full_filename) |
|
283 | self.shell.safe_execfile_ipy(full_filename) | |
283 | else: |
|
284 | else: | |
284 | # default to python, even without extension |
|
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 | finally: |
|
294 | finally: | |
287 | sys.argv = save_argv |
|
295 | sys.argv = save_argv | |
288 |
|
296 |
@@ -87,6 +87,18 b' def test_history():' | |||||
87 | # Check get_hist_search |
|
87 | # Check get_hist_search | |
88 | gothist = ip.history_manager.search("*test*") |
|
88 | gothist = ip.history_manager.search("*test*") | |
89 | nt.assert_equal(list(gothist), [(1,2,hist[1])] ) |
|
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 | gothist = ip.history_manager.search("b*", output=True) |
|
102 | gothist = ip.history_manager.search("b*", output=True) | |
91 | nt.assert_equal(list(gothist), [(1,3,(hist[2],"spam"))] ) |
|
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 | @py3compat.doctest_refactor_print |
|
139 | @py3compat.doctest_refactor_print | |
90 | def doctest_reset_del(): |
|
140 | def doctest_reset_del(): | |
91 | """Test that resetting doesn't cause errors in __del__ methods. |
|
141 | """Test that resetting doesn't cause errors in __del__ methods. |
@@ -1,6 +1,7 b'' | |||||
|
1 | # encoding: utf-8 | |||
1 | """Tests for IPython.core.ultratb |
|
2 | """Tests for IPython.core.ultratb | |
2 | """ |
|
3 | """ | |
3 |
|
4 | import io | ||
4 | import os.path |
|
5 | import os.path | |
5 | import unittest |
|
6 | import unittest | |
6 |
|
7 | |||
@@ -49,3 +50,52 b' class ChangedPyFileTest(unittest.TestCase):' | |||||
49 | ip.run_cell("foo.f()") |
|
50 | ip.run_cell("foo.f()") | |
50 | with tt.AssertPrints("ZeroDivisionError"): |
|
51 | with tt.AssertPrints("ZeroDivisionError"): | |
51 | ip.run_cell("foo.f()") |
|
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 | # the file COPYING, distributed as part of this software. |
|
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 | import inspect |
|
74 | import inspect | |
75 | import keyword |
|
75 | import keyword | |
@@ -99,9 +99,12 b' from IPython.core.display_trap import DisplayTrap' | |||||
99 | from IPython.core.excolors import exception_colors |
|
99 | from IPython.core.excolors import exception_colors | |
100 | from IPython.utils import PyColorize |
|
100 | from IPython.utils import PyColorize | |
101 | from IPython.utils import io |
|
101 | from IPython.utils import io | |
|
102 | from IPython.utils import path as util_path | |||
102 | from IPython.utils import py3compat |
|
103 | from IPython.utils import py3compat | |
103 | from IPython.utils import pyfile |
|
104 | from IPython.utils import pyfile | |
|
105 | from IPython.utils import ulinecache | |||
104 | from IPython.utils.data import uniq_stable |
|
106 | from IPython.utils.data import uniq_stable | |
|
107 | from IPython.utils.openpy import read_py_file | |||
105 | from IPython.utils.warn import info, error |
|
108 | from IPython.utils.warn import info, error | |
106 |
|
109 | |||
107 | # Globals |
|
110 | # Globals | |
@@ -229,7 +232,6 b' def fix_frame_records_filenames(records):' | |||||
229 |
|
232 | |||
230 |
|
233 | |||
231 | def _fixed_getinnerframes(etb, context=1,tb_offset=0): |
|
234 | def _fixed_getinnerframes(etb, context=1,tb_offset=0): | |
232 | import linecache |
|
|||
233 | LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5 |
|
235 | LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5 | |
234 |
|
236 | |||
235 | records = fix_frame_records_filenames(inspect.getinnerframes(etb, context)) |
|
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 | maybeStart = lnum-1 - context//2 |
|
253 | maybeStart = lnum-1 - context//2 | |
252 | start = max(maybeStart, 0) |
|
254 | start = max(maybeStart, 0) | |
253 | end = start + context |
|
255 | end = start + context | |
254 | lines = linecache.getlines(file)[start:end] |
|
256 | lines = ulinecache.getlines(file)[start:end] | |
255 | buf = list(records[i]) |
|
257 | buf = list(records[i]) | |
256 | buf[LNUM_POS] = lnum |
|
258 | buf[LNUM_POS] = lnum | |
257 | buf[INDEX_POS] = lnum - 1 - start |
|
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 | _line_format = _parser.format2 |
|
284 | _line_format = _parser.format2 | |
283 |
|
285 | |||
284 | for line in lines: |
|
286 | for line in lines: | |
285 | # FIXME: we need to ensure the source is a pure string at this point, |
|
287 | line = py3compat.cast_unicode(line) | |
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') |
|
|||
291 |
|
288 | |||
292 | new_line, err = _line_format(line, 'str', scheme) |
|
289 | new_line, err = _line_format(line, 'str', scheme) | |
293 | if not err: line = new_line |
|
290 | if not err: line = new_line | |
@@ -547,33 +544,41 b' class ListTB(TBTools):' | |||||
547 |
|
544 | |||
548 | Also lifted nearly verbatim from traceback.py |
|
545 | Also lifted nearly verbatim from traceback.py | |
549 | """ |
|
546 | """ | |
550 |
|
||||
551 | have_filedata = False |
|
547 | have_filedata = False | |
552 | Colors = self.Colors |
|
548 | Colors = self.Colors | |
553 | list = [] |
|
549 | list = [] | |
554 | stype = Colors.excName + etype.__name__ + Colors.Normal |
|
550 | stype = Colors.excName + etype.__name__ + Colors.Normal | |
555 | if value is None: |
|
551 | if value is None: | |
556 | # Not sure if this can still happen in Python 2.6 and above |
|
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 | else: |
|
554 | else: | |
559 |
if etype |
|
555 | if issubclass(etype, SyntaxError): | |
560 | have_filedata = True |
|
556 | have_filedata = True | |
561 | #print 'filename is',filename # dbg |
|
557 | #print 'filename is',filename # dbg | |
562 | if not value.filename: value.filename = "<string>" |
|
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 | (Colors.normalEm, |
|
566 | (Colors.normalEm, | |
565 | Colors.filenameEm, value.filename, Colors.normalEm, |
|
567 | Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm, | |
566 |
Colors.linenoEm, |
|
568 | Colors.linenoEm, lineno, Colors.Normal )) | |
567 |
if |
|
569 | if textline == '': | |
|
570 | textline = py3compat.cast_unicode(value.text, "utf-8") | |||
|
571 | ||||
|
572 | if textline is not None: | |||
568 | i = 0 |
|
573 | i = 0 | |
569 |
while i < len( |
|
574 | while i < len(textline) and textline[i].isspace(): | |
570 | i += 1 |
|
575 | i += 1 | |
571 | list.append('%s %s%s\n' % (Colors.line, |
|
576 | list.append('%s %s%s\n' % (Colors.line, | |
572 |
|
|
577 | textline.strip(), | |
573 | Colors.Normal)) |
|
578 | Colors.Normal)) | |
574 | if value.offset is not None: |
|
579 | if value.offset is not None: | |
575 | s = ' ' |
|
580 | s = ' ' | |
576 |
for c in |
|
581 | for c in textline[i:value.offset-1]: | |
577 | if c.isspace(): |
|
582 | if c.isspace(): | |
578 | s += c |
|
583 | s += c | |
579 | else: |
|
584 | else: | |
@@ -779,10 +784,9 b' class VerboseTB(TBTools):' | |||||
779 | abspath = os.path.abspath |
|
784 | abspath = os.path.abspath | |
780 | for frame, file, lnum, func, lines, index in records: |
|
785 | for frame, file, lnum, func, lines, index in records: | |
781 | #print '*** record:',file,lnum,func,lines,index # dbg |
|
786 | #print '*** record:',file,lnum,func,lines,index # dbg | |
782 |
|
||||
783 | if not file: |
|
787 | if not file: | |
784 | file = '?' |
|
788 | file = '?' | |
785 | elif not(file.startswith("<") and file.endswith(">")): |
|
789 | elif not(file.startswith(str("<")) and file.endswith(str(">"))): | |
786 | # Guess that filenames like <string> aren't real filenames, so |
|
790 | # Guess that filenames like <string> aren't real filenames, so | |
787 | # don't call abspath on them. |
|
791 | # don't call abspath on them. | |
788 | try: |
|
792 | try: | |
@@ -791,7 +795,7 b' class VerboseTB(TBTools):' | |||||
791 | # Not sure if this can still happen: abspath now works with |
|
795 | # Not sure if this can still happen: abspath now works with | |
792 | # file names like <string> |
|
796 | # file names like <string> | |
793 | pass |
|
797 | pass | |
794 |
|
798 | file = py3compat.cast_unicode(file, util_path.fs_encoding) | ||
795 | link = tpl_link % file |
|
799 | link = tpl_link % file | |
796 | args, varargs, varkw, locals = inspect.getargvalues(frame) |
|
800 | args, varargs, varkw, locals = inspect.getargvalues(frame) | |
797 |
|
801 | |||
@@ -831,7 +835,7 b' class VerboseTB(TBTools):' | |||||
831 | # Look up the corresponding source file. |
|
835 | # Look up the corresponding source file. | |
832 | file = pyfile.source_from_cache(file) |
|
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 | line = getline(file, lnum[0]) |
|
839 | line = getline(file, lnum[0]) | |
836 | lnum[0] += 1 |
|
840 | lnum[0] += 1 | |
837 | return line |
|
841 | return line | |
@@ -926,7 +930,7 b' class VerboseTB(TBTools):' | |||||
926 | etype_str,evalue_str = map(str,(etype,evalue)) |
|
930 | etype_str,evalue_str = map(str,(etype,evalue)) | |
927 | # ... and format it |
|
931 | # ... and format it | |
928 | exception = ['%s%s%s: %s' % (Colors.excName, etype_str, |
|
932 | exception = ['%s%s%s: %s' % (Colors.excName, etype_str, | |
929 | ColorsNormal, evalue_str)] |
|
933 | ColorsNormal, py3compat.cast_unicode(evalue_str))] | |
930 | if (not py3compat.PY3) and type(evalue) is types.InstanceType: |
|
934 | if (not py3compat.PY3) and type(evalue) is types.InstanceType: | |
931 | try: |
|
935 | try: | |
932 | names = [w for w in dir(evalue) if isinstance(w, basestring)] |
|
936 | names = [w for w in dir(evalue) if isinstance(w, basestring)] | |
@@ -938,7 +942,7 b' class VerboseTB(TBTools):' | |||||
938 | exception.append(_m % (Colors.excName,ColorsNormal)) |
|
942 | exception.append(_m % (Colors.excName,ColorsNormal)) | |
939 | etype_str,evalue_str = map(str,sys.exc_info()[:2]) |
|
943 | etype_str,evalue_str = map(str,sys.exc_info()[:2]) | |
940 | exception.append('%s%s%s: %s' % (Colors.excName,etype_str, |
|
944 | exception.append('%s%s%s: %s' % (Colors.excName,etype_str, | |
941 | ColorsNormal, evalue_str)) |
|
945 | ColorsNormal, py3compat.cast_unicode(evalue_str))) | |
942 | names = [] |
|
946 | names = [] | |
943 | for name in names: |
|
947 | for name in names: | |
944 | value = text_repr(getattr(evalue, name)) |
|
948 | value = text_repr(getattr(evalue, name)) |
@@ -106,13 +106,10 b' skip_doctest = True' | |||||
106 | #----------------------------------------------------------------------------- |
|
106 | #----------------------------------------------------------------------------- | |
107 | # Imports |
|
107 | # Imports | |
108 | #----------------------------------------------------------------------------- |
|
108 | #----------------------------------------------------------------------------- | |
109 | import atexit |
|
109 | ||
110 | import imp |
|
110 | import imp | |
111 | import inspect |
|
|||
112 | import os |
|
111 | import os | |
113 | import sys |
|
112 | import sys | |
114 | import threading |
|
|||
115 | import time |
|
|||
116 | import traceback |
|
113 | import traceback | |
117 | import types |
|
114 | import types | |
118 | import weakref |
|
115 | import weakref | |
@@ -409,7 +406,6 b' def superreload(module, reload=reload, old_objects={}):' | |||||
409 |
|
406 | |||
410 | from IPython.core.hooks import TryNext |
|
407 | from IPython.core.hooks import TryNext | |
411 | from IPython.core.magic import Magics, magics_class, line_magic |
|
408 | from IPython.core.magic import Magics, magics_class, line_magic | |
412 | from IPython.core.plugin import Plugin |
|
|||
413 |
|
409 | |||
414 | @magics_class |
|
410 | @magics_class | |
415 | class AutoreloadMagics(Magics): |
|
411 | class AutoreloadMagics(Magics): | |
@@ -518,21 +514,8 b' class AutoreloadMagics(Magics):' | |||||
518 | pass |
|
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 | def load_ipython_extension(ip): |
|
517 | def load_ipython_extension(ip): | |
533 | """Load the extension in IPython.""" |
|
518 | """Load the extension in IPython.""" | |
534 | global _loaded |
|
519 | auto_reload = AutoreloadMagics(ip) | |
535 | if not _loaded: |
|
520 | ip.register_magics(auto_reload) | |
536 | plugin = AutoreloadPlugin(shell=ip, config=ip.config) |
|
521 | ip.set_hook('pre_run_code_hook', auto_reload.pre_run_code_hook) | |
537 | ip.plugin_manager.register_plugin('autoreload', plugin) |
|
|||
538 | _loaded = True |
|
@@ -125,6 +125,11 b' class CythonMagics(Magics):' | |||||
125 | "multiple times)." |
|
125 | "multiple times)." | |
126 | ) |
|
126 | ) | |
127 | @magic_arguments.argument( |
|
127 | @magic_arguments.argument( | |
|
128 | '-L', dest='library_dirs', metavar='dir', action='append', default=[], | |||
|
129 | help="Add a path to the list of libary directories (can be specified " | |||
|
130 | "multiple times)." | |||
|
131 | ) | |||
|
132 | @magic_arguments.argument( | |||
128 | '-I', '--include', action='append', default=[], |
|
133 | '-I', '--include', action='append', default=[], | |
129 | help="Add a path to the list of include directories (can be specified " |
|
134 | help="Add a path to the list of include directories (can be specified " | |
130 | "multiple times)." |
|
135 | "multiple times)." | |
@@ -195,6 +200,7 b' class CythonMagics(Magics):' | |||||
195 | name = module_name, |
|
200 | name = module_name, | |
196 | sources = [pyx_file], |
|
201 | sources = [pyx_file], | |
197 | include_dirs = c_include_dirs, |
|
202 | include_dirs = c_include_dirs, | |
|
203 | library_dirs = args.library_dirs, | |||
198 | extra_compile_args = args.compile_args, |
|
204 | extra_compile_args = args.compile_args, | |
199 | extra_link_args = args.link_args, |
|
205 | extra_link_args = args.link_args, | |
200 | libraries = args.lib, |
|
206 | libraries = args.lib, | |
@@ -267,11 +273,7 b' class CythonMagics(Magics):' | |||||
267 | html = '\n'.join(l for l in html.splitlines() if not r.match(l)) |
|
273 | html = '\n'.join(l for l in html.splitlines() if not r.match(l)) | |
268 | return html |
|
274 | return html | |
269 |
|
275 | |||
270 | _loaded = False |
|
|||
271 |
|
276 | |||
272 | def load_ipython_extension(ip): |
|
277 | def load_ipython_extension(ip): | |
273 | """Load the extension in IPython.""" |
|
278 | """Load the extension in IPython.""" | |
274 | global _loaded |
|
279 | ip.register_magics(CythonMagics) | |
275 | if not _loaded: |
|
|||
276 | ip.register_magics(CythonMagics) |
|
|||
277 | _loaded = True |
|
@@ -362,10 +362,6 b' __doc__ = __doc__.format(' | |||||
362 | ) |
|
362 | ) | |
363 |
|
363 | |||
364 |
|
364 | |||
365 | _loaded = False |
|
|||
366 | def load_ipython_extension(ip): |
|
365 | def load_ipython_extension(ip): | |
367 | """Load the extension in IPython.""" |
|
366 | """Load the extension in IPython.""" | |
368 | global _loaded |
|
367 | ip.register_magics(OctaveMagics) | |
369 | if not _loaded: |
|
|||
370 | ip.register_magics(OctaveMagics) |
|
|||
371 | _loaded = True |
|
@@ -588,10 +588,6 b' __doc__ = __doc__.format(' | |||||
588 | ) |
|
588 | ) | |
589 |
|
589 | |||
590 |
|
590 | |||
591 | _loaded = False |
|
|||
592 | def load_ipython_extension(ip): |
|
591 | def load_ipython_extension(ip): | |
593 | """Load the extension in IPython.""" |
|
592 | """Load the extension in IPython.""" | |
594 | global _loaded |
|
593 | ip.register_magics(RMagics) | |
595 | if not _loaded: |
|
|||
596 | ip.register_magics(RMagics) |
|
|||
597 | _loaded = True |
|
@@ -28,9 +28,7 b' import inspect, os, sys, textwrap' | |||||
28 | from IPython.core.error import UsageError |
|
28 | from IPython.core.error import UsageError | |
29 | from IPython.core.fakemodule import FakeModule |
|
29 | from IPython.core.fakemodule import FakeModule | |
30 | from IPython.core.magic import Magics, magics_class, line_magic |
|
30 | from IPython.core.magic import Magics, magics_class, line_magic | |
31 | from IPython.core.plugin import Plugin |
|
|||
32 | from IPython.testing.skipdoctest import skip_doctest |
|
31 | from IPython.testing.skipdoctest import skip_doctest | |
33 | from IPython.utils.traitlets import Bool, Instance |
|
|||
34 |
|
32 | |||
35 | #----------------------------------------------------------------------------- |
|
33 | #----------------------------------------------------------------------------- | |
36 | # Functions and classes |
|
34 | # Functions and classes | |
@@ -211,24 +209,6 b' class StoreMagics(Magics):' | |||||
211 | print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__) |
|
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 | def load_ipython_extension(ip): |
|
212 | def load_ipython_extension(ip): | |
229 | """Load the extension in IPython.""" |
|
213 | """Load the extension in IPython.""" | |
230 | global _loaded |
|
214 | ip.register_magics(StoreMagics) | |
231 | if not _loaded: |
|
|||
232 | plugin = StoreMagic(shell=ip, config=ip.config) |
|
|||
233 | ip.plugin_manager.register_plugin('storemagic', plugin) |
|
|||
234 | _loaded = True |
|
@@ -9,6 +9,11 b' Usage' | |||||
9 | Once the extension is loaded, Sympy Basic objects are automatically |
|
9 | Once the extension is loaded, Sympy Basic objects are automatically | |
10 | pretty-printed. |
|
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 | # Copyright (C) 2008 The IPython Development Team |
|
19 | # Copyright (C) 2008 The IPython Development Team | |
@@ -30,6 +35,8 b' try:' | |||||
30 | except ImportError: |
|
35 | except ImportError: | |
31 | pass |
|
36 | pass | |
32 |
|
37 | |||
|
38 | import warnings | |||
|
39 | ||||
33 | #----------------------------------------------------------------------------- |
|
40 | #----------------------------------------------------------------------------- | |
34 | # Definitions of special display functions for use with IPython |
|
41 | # Definitions of special display functions for use with IPython | |
35 | #----------------------------------------------------------------------------- |
|
42 | #----------------------------------------------------------------------------- | |
@@ -101,6 +108,19 b' _loaded = False' | |||||
101 | def load_ipython_extension(ip): |
|
108 | def load_ipython_extension(ip): | |
102 | """Load the extension in IPython.""" |
|
109 | """Load the extension in IPython.""" | |
103 | import sympy |
|
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 | global _loaded |
|
124 | global _loaded | |
105 | if not _loaded: |
|
125 | if not _loaded: | |
106 | plaintext_formatter = ip.display_formatter.formatters['text/plain'] |
|
126 | plaintext_formatter = ip.display_formatter.formatters['text/plain'] |
@@ -23,7 +23,7 b' from StringIO import StringIO' | |||||
23 | import nose.tools as nt |
|
23 | import nose.tools as nt | |
24 | import IPython.testing.tools as tt |
|
24 | import IPython.testing.tools as tt | |
25 |
|
25 | |||
26 |
from IPython.extensions.autoreload import Autoreload |
|
26 | from IPython.extensions.autoreload import AutoreloadMagics | |
27 | from IPython.core.hooks import TryNext |
|
27 | from IPython.core.hooks import TryNext | |
28 |
|
28 | |||
29 | #----------------------------------------------------------------------------- |
|
29 | #----------------------------------------------------------------------------- | |
@@ -35,13 +35,13 b' noop = lambda *a, **kw: None' | |||||
35 | class FakeShell(object): |
|
35 | class FakeShell(object): | |
36 | def __init__(self): |
|
36 | def __init__(self): | |
37 | self.ns = {} |
|
37 | self.ns = {} | |
38 |
self. |
|
38 | self.auto_magics = AutoreloadMagics(shell=self) | |
39 |
|
39 | |||
40 | register_magics = set_hook = noop |
|
40 | register_magics = set_hook = noop | |
41 |
|
41 | |||
42 | def run_code(self, code): |
|
42 | def run_code(self, code): | |
43 | try: |
|
43 | try: | |
44 |
self |
|
44 | self.auto_magics.pre_run_code_hook(self) | |
45 | except TryNext: |
|
45 | except TryNext: | |
46 | pass |
|
46 | pass | |
47 | exec code in self.ns |
|
47 | exec code in self.ns | |
@@ -50,10 +50,10 b' class FakeShell(object):' | |||||
50 | self.ns.update(items) |
|
50 | self.ns.update(items) | |
51 |
|
51 | |||
52 | def magic_autoreload(self, parameter): |
|
52 | def magic_autoreload(self, parameter): | |
53 |
self |
|
53 | self.auto_magics.autoreload(parameter) | |
54 |
|
54 | |||
55 | def magic_aimport(self, parameter, stream=None): |
|
55 | def magic_aimport(self, parameter, stream=None): | |
56 |
self |
|
56 | self.auto_magics.aimport(parameter, stream=stream) | |
57 |
|
57 | |||
58 |
|
58 | |||
59 | class Fixture(object): |
|
59 | class Fixture(object): | |
@@ -72,7 +72,6 b' class Fixture(object):' | |||||
72 | def tearDown(self): |
|
72 | def tearDown(self): | |
73 | shutil.rmtree(self.test_dir) |
|
73 | shutil.rmtree(self.test_dir) | |
74 | sys.path = self.old_sys_path |
|
74 | sys.path = self.old_sys_path | |
75 | self.shell.reloader.enabled = False |
|
|||
76 |
|
75 | |||
77 | self.test_dir = None |
|
76 | self.test_dir = None | |
78 | self.old_sys_path = None |
|
77 | self.old_sys_path = None |
@@ -860,10 +860,10 b' class path(unicode):' | |||||
860 |
|
860 | |||
861 | # --- Create/delete operations on directories |
|
861 | # --- Create/delete operations on directories | |
862 |
|
862 | |||
863 | def mkdir(self, mode=0777): |
|
863 | def mkdir(self, mode=0o777): | |
864 | os.mkdir(self, mode) |
|
864 | os.mkdir(self, mode) | |
865 |
|
865 | |||
866 | def makedirs(self, mode=0777): |
|
866 | def makedirs(self, mode=0o777): | |
867 | os.makedirs(self, mode) |
|
867 | os.makedirs(self, mode) | |
868 |
|
868 | |||
869 | def rmdir(self): |
|
869 | def rmdir(self): | |
@@ -879,7 +879,7 b' class path(unicode):' | |||||
879 | """ Set the access/modified times of this file to the current time. |
|
879 | """ Set the access/modified times of this file to the current time. | |
880 | Create the file if it does not exist. |
|
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 | os.close(fd) |
|
883 | os.close(fd) | |
884 | os.utime(self, None) |
|
884 | os.utime(self, None) | |
885 |
|
885 |
@@ -22,6 +22,7 b' Authors' | |||||
22 | from __future__ import print_function |
|
22 | from __future__ import print_function | |
23 |
|
23 | |||
24 | import os,sys, atexit |
|
24 | import os,sys, atexit | |
|
25 | import signal | |||
25 | import socket |
|
26 | import socket | |
26 | from multiprocessing import Process |
|
27 | from multiprocessing import Process | |
27 | from getpass import getpass, getuser |
|
28 | from getpass import getpass, getuser | |
@@ -331,9 +332,10 b' def _paramiko_tunnel(lport, rport, server, remoteip, keyfile=None, password=None' | |||||
331 | except Exception as e: |
|
332 | except Exception as e: | |
332 | print ('*** Failed to connect to %s:%d: %r' % (server, port, e)) |
|
333 | print ('*** Failed to connect to %s:%d: %r' % (server, port, e)) | |
333 | sys.exit(1) |
|
334 | sys.exit(1) | |
334 |
|
335 | |||
335 | # print ('Now forwarding port %d to %s:%d ...' % (lport, server, rport)) |
|
336 | # Don't let SIGINT kill the tunnel subprocess | |
336 |
|
337 | signal.signal(signal.SIGINT, signal.SIG_IGN) | ||
|
338 | ||||
337 | try: |
|
339 | try: | |
338 | forward_tunnel(lport, remoteip, rport, client.get_transport()) |
|
340 | forward_tunnel(lport, remoteip, rport, client.get_transport()) | |
339 | except KeyboardInterrupt: |
|
341 | except KeyboardInterrupt: |
@@ -24,6 +24,7 b' Authors:' | |||||
24 | import atexit |
|
24 | import atexit | |
25 | import json |
|
25 | import json | |
26 | import os |
|
26 | import os | |
|
27 | import shutil | |||
27 | import signal |
|
28 | import signal | |
28 | import sys |
|
29 | import sys | |
29 | import uuid |
|
30 | import uuid | |
@@ -38,7 +39,7 b' from IPython.zmq.blockingkernelmanager import BlockingKernelManager' | |||||
38 | from IPython.utils.path import filefind |
|
39 | from IPython.utils.path import filefind | |
39 | from IPython.utils.py3compat import str_to_bytes |
|
40 | from IPython.utils.py3compat import str_to_bytes | |
40 | from IPython.utils.traitlets import ( |
|
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 | from IPython.zmq.ipkernel import ( |
|
44 | from IPython.zmq.ipkernel import ( | |
44 | flags as ipkernel_flags, |
|
45 | flags as ipkernel_flags, | |
@@ -151,12 +152,27 b' class IPythonConsoleApp(Configurable):' | |||||
151 | # create requested profiles by default, if they don't exist: |
|
152 | # create requested profiles by default, if they don't exist: | |
152 | auto_create = CBool(True) |
|
153 | auto_create = CBool(True) | |
153 | # connection info: |
|
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 | help="""Set the kernel\'s IP address [default localhost]. |
|
159 | help="""Set the kernel\'s IP address [default localhost]. | |
156 | If the IP address is something other than localhost, then |
|
160 | If the IP address is something other than localhost, then | |
157 | Consoles on other machines will be able to connect |
|
161 | Consoles on other machines will be able to connect | |
158 | to the Kernel, so be careful!""" |
|
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 | sshserver = Unicode('', config=True, |
|
177 | sshserver = Unicode('', config=True, | |
162 | help="""The SSH server to use to connect to the kernel.""") |
|
178 | help="""The SSH server to use to connect to the kernel.""") | |
@@ -256,10 +272,10 b' class IPythonConsoleApp(Configurable):' | |||||
256 | return |
|
272 | return | |
257 | self.log.debug(u"Loading connection file %s", fname) |
|
273 | self.log.debug(u"Loading connection file %s", fname) | |
258 | with open(fname) as f: |
|
274 | with open(fname) as f: | |
259 |
|
|
275 | cfg = json.load(f) | |
260 | cfg = json.loads(s) |
|
276 | ||
261 | if self.ip == LOCALHOST and 'ip' in cfg: |
|
277 | self.transport = cfg.get('transport', 'tcp') | |
262 | # not overridden by config or cl_args |
|
278 | if 'ip' in cfg: | |
263 | self.ip = cfg['ip'] |
|
279 | self.ip = cfg['ip'] | |
264 | for channel in ('hb', 'shell', 'iopub', 'stdin'): |
|
280 | for channel in ('hb', 'shell', 'iopub', 'stdin'): | |
265 | name = channel + '_port' |
|
281 | name = channel + '_port' | |
@@ -268,12 +284,17 b' class IPythonConsoleApp(Configurable):' | |||||
268 | setattr(self, name, cfg[name]) |
|
284 | setattr(self, name, cfg[name]) | |
269 | if 'key' in cfg: |
|
285 | if 'key' in cfg: | |
270 | self.config.Session.key = str_to_bytes(cfg['key']) |
|
286 | self.config.Session.key = str_to_bytes(cfg['key']) | |
|
287 | ||||
271 |
|
288 | |||
272 | def init_ssh(self): |
|
289 | def init_ssh(self): | |
273 | """set up ssh tunnels, if needed.""" |
|
290 | """set up ssh tunnels, if needed.""" | |
274 | if not self.sshserver and not self.sshkey: |
|
291 | if not self.sshserver and not self.sshkey: | |
275 | return |
|
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 | if self.sshkey and not self.sshserver: |
|
298 | if self.sshkey and not self.sshserver: | |
278 | # specifying just the key implies that we are connecting directly |
|
299 | # specifying just the key implies that we are connecting directly | |
279 | self.sshserver = self.ip |
|
300 | self.sshserver = self.ip | |
@@ -326,6 +347,7 b' class IPythonConsoleApp(Configurable):' | |||||
326 |
|
347 | |||
327 | # Create a KernelManager and start a kernel. |
|
348 | # Create a KernelManager and start a kernel. | |
328 | self.kernel_manager = self.kernel_manager_class( |
|
349 | self.kernel_manager = self.kernel_manager_class( | |
|
350 | transport=self.transport, | |||
329 | ip=self.ip, |
|
351 | ip=self.ip, | |
330 | shell_port=self.shell_port, |
|
352 | shell_port=self.shell_port, | |
331 | iopub_port=self.iopub_port, |
|
353 | iopub_port=self.iopub_port, |
@@ -149,13 +149,13 b' class AuthenticatedHandler(RequestHandler):' | |||||
149 | """A RequestHandler with an authenticated user.""" |
|
149 | """A RequestHandler with an authenticated user.""" | |
150 |
|
150 | |||
151 | def get_current_user(self): |
|
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 | # For now the user_id should not return empty, but it could eventually |
|
153 | # For now the user_id should not return empty, but it could eventually | |
154 | if user_id == '': |
|
154 | if user_id == '': | |
155 | user_id = 'anonymous' |
|
155 | user_id = 'anonymous' | |
156 | if user_id is None: |
|
156 | if user_id is None: | |
157 | # prevent extra Invalid cookie sig warnings: |
|
157 | # prevent extra Invalid cookie sig warnings: | |
158 |
self.clear_cookie(' |
|
158 | self.clear_cookie(self.settings['cookie_name']) | |
159 | if not self.application.password and not self.application.read_only: |
|
159 | if not self.application.password and not self.application.read_only: | |
160 | user_id = 'anonymous' |
|
160 | user_id = 'anonymous' | |
161 | return user_id |
|
161 | return user_id | |
@@ -247,7 +247,7 b' class LoginHandler(AuthenticatedHandler):' | |||||
247 | pwd = self.get_argument('password', default=u'') |
|
247 | pwd = self.get_argument('password', default=u'') | |
248 | if self.application.password: |
|
248 | if self.application.password: | |
249 | if passwd_check(self.application.password, pwd): |
|
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 | else: |
|
251 | else: | |
252 | self._render(message={'error': 'Invalid password'}) |
|
252 | self._render(message={'error': 'Invalid password'}) | |
253 | return |
|
253 | return | |
@@ -258,7 +258,7 b' class LoginHandler(AuthenticatedHandler):' | |||||
258 | class LogoutHandler(AuthenticatedHandler): |
|
258 | class LogoutHandler(AuthenticatedHandler): | |
259 |
|
259 | |||
260 | def get(self): |
|
260 | def get(self): | |
261 |
self.clear_cookie(' |
|
261 | self.clear_cookie(self.settings['cookie_name']) | |
262 | if self.login_available: |
|
262 | if self.login_available: | |
263 | message = {'info': 'Successfully logged out.'} |
|
263 | message = {'info': 'Successfully logged out.'} | |
264 | else: |
|
264 | else: | |
@@ -435,7 +435,7 b' class AuthenticatedZMQStreamHandler(ZMQStreamHandler):' | |||||
435 | self.on_message = self.on_first_message |
|
435 | self.on_message = self.on_first_message | |
436 |
|
436 | |||
437 | def get_current_user(self): |
|
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 | if user_id == '' or (user_id is None and not self.application.password): |
|
439 | if user_id == '' or (user_id is None and not self.application.password): | |
440 | user_id = 'anonymous' |
|
440 | user_id = 'anonymous' | |
441 | return user_id |
|
441 | return user_id |
@@ -42,6 +42,11 b' class NotebookManager(LoggingConfigurable):' | |||||
42 | """) |
|
42 | """) | |
43 | def _notebook_dir_changed(self, name, old, new): |
|
43 | def _notebook_dir_changed(self, name, old, new): | |
44 | """do a bit of validation of the notebook dir""" |
|
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 | if os.path.exists(new) and not os.path.isdir(new): |
|
50 | if os.path.exists(new) and not os.path.isdir(new): | |
46 | raise TraitError("notebook dir %r is not a directory" % new) |
|
51 | raise TraitError("notebook dir %r is not a directory" % new) | |
47 | if not os.path.exists(new): |
|
52 | if not os.path.exists(new): | |
@@ -117,7 +122,7 b' class NotebookManager(LoggingConfigurable):' | |||||
117 | # should match the Python in-memory format. |
|
122 | # should match the Python in-memory format. | |
118 | kwargs['split_lines'] = False |
|
123 | kwargs['split_lines'] = False | |
119 | data = current.writes(nb, format, **kwargs) |
|
124 | data = current.writes(nb, format, **kwargs) | |
120 | name = nb.get('name','notebook') |
|
125 | name = nb.metadata.get('name','notebook') | |
121 | return last_modified, name, data |
|
126 | return last_modified, name, data | |
122 |
|
127 | |||
123 | def read_notebook_object(self, notebook_id): |
|
128 | def read_notebook_object(self, notebook_id): |
@@ -28,6 +28,7 b' import socket' | |||||
28 | import sys |
|
28 | import sys | |
29 | import threading |
|
29 | import threading | |
30 | import time |
|
30 | import time | |
|
31 | import uuid | |||
31 | import webbrowser |
|
32 | import webbrowser | |
32 |
|
33 | |||
33 | # Third party |
|
34 | # Third party | |
@@ -164,6 +165,7 b' class NotebookWebApplication(web.Application):' | |||||
164 | static_handler_class = FileFindHandler, |
|
165 | static_handler_class = FileFindHandler, | |
165 | cookie_secret=os.urandom(1024), |
|
166 | cookie_secret=os.urandom(1024), | |
166 | login_url="%s/login"%(base_project_url.rstrip('/')), |
|
167 | login_url="%s/login"%(base_project_url.rstrip('/')), | |
|
168 | cookie_name='username-%s' % uuid.uuid4(), | |||
167 | ) |
|
169 | ) | |
168 |
|
170 | |||
169 | # allow custom overrides for the tornado web app. |
|
171 | # allow custom overrides for the tornado web app. | |
@@ -468,11 +470,14 b' class NotebookApp(BaseIPythonApplication):' | |||||
468 | ssl_options = None |
|
470 | ssl_options = None | |
469 | self.web_app.password = self.password |
|
471 | self.web_app.password = self.password | |
470 | self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options) |
|
472 | self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options) | |
471 | if ssl_options is None and not self.ip and not (self.read_only and not self.password): |
|
473 | if not self.ip: | |
472 |
|
|
474 | warning = "WARNING: The notebook server is listening on all IP addresses" | |
473 | 'but not using any encryption or authentication. This is highly ' |
|
475 | if ssl_options is None: | |
474 | 'insecure and not recommended.') |
|
476 | self.log.critical(warning + " and not using encryption. This" | |
475 |
|
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 | success = None |
|
481 | success = None | |
477 | for port in random_ports(self.port, self.port_retries+1): |
|
482 | for port in random_ports(self.port, self.port_retries+1): | |
478 | try: |
|
483 | try: |
@@ -70,7 +70,7 b' span#notebook_name {' | |||||
70 | z-index: 10; |
|
70 | z-index: 10; | |
71 | } |
|
71 | } | |
72 |
|
72 | |||
73 |
|
|
73 | .toolbar { | |
74 | padding: 3px 15px; |
|
74 | padding: 3px 15px; | |
75 | } |
|
75 | } | |
76 |
|
76 | |||
@@ -122,6 +122,10 b' div#pager_splitter {' | |||||
122 | height: 8px; |
|
122 | height: 8px; | |
123 | } |
|
123 | } | |
124 |
|
124 | |||
|
125 | #pager_container { | |||
|
126 | position : relative; | |||
|
127 | } | |||
|
128 | ||||
125 | div#pager { |
|
129 | div#pager { | |
126 | padding: 15px; |
|
130 | padding: 15px; | |
127 | overflow: auto; |
|
131 | overflow: auto; | |
@@ -234,7 +238,7 b' div.output_area {' | |||||
234 | /* This class is for the output subarea inside the output_area and after |
|
238 | /* This class is for the output subarea inside the output_area and after | |
235 | the prompt div. */ |
|
239 | the prompt div. */ | |
236 | div.output_subarea { |
|
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 | /* The rest of the output_* classes are for special styling of the different |
|
244 | /* The rest of the output_* classes are for special styling of the different |
@@ -33,7 +33,7 b' span#ipython_notebook {' | |||||
33 | padding: 2px 2px 2px 5px; |
|
33 | padding: 2px 2px 2px 5px; | |
34 | } |
|
34 | } | |
35 |
|
35 | |||
36 |
span#ipython_notebook |
|
36 | span#ipython_notebook img { | |
37 | font-family: Verdana, "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif; |
|
37 | font-family: Verdana, "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif; | |
38 | height: 24px; |
|
38 | height: 24px; | |
39 | text-decoration:none; |
|
39 | text-decoration:none; |
@@ -44,7 +44,7 b'' | |||||
44 | opacity: 1; |
|
44 | opacity: 1; | |
45 | } |
|
45 | } | |
46 | } |
|
46 | } | |
47 | .tooltip a { |
|
47 | .ipython_tooltip a { | |
48 | float: right; |
|
48 | float: right; | |
49 | } |
|
49 | } | |
50 | /*properties of tooltip after "expand"*/ |
|
50 | /*properties of tooltip after "expand"*/ | |
@@ -81,7 +81,7 b'' | |||||
81 |
|
81 | |||
82 | padding-right: 30px; |
|
82 | padding-right: 30px; | |
83 | } |
|
83 | } | |
84 | .tooltip { |
|
84 | .ipython_tooltip { | |
85 | max-width: 700px; |
|
85 | max-width: 700px; | |
86 | border-radius: 4px; |
|
86 | border-radius: 4px; | |
87 | -moz-box-shadow: 0px 6px 10px -1px #adadad; |
|
87 | -moz-box-shadow: 0px 6px 10px -1px #adadad; |
@@ -20,6 +20,8 b' var IPython = (function (IPython) {' | |||||
20 | this.selected = false; |
|
20 | this.selected = false; | |
21 | this.element = null; |
|
21 | this.element = null; | |
22 | this.metadata = {}; |
|
22 | this.metadata = {}; | |
|
23 | // load this from metadata later ? | |||
|
24 | this.user_highlight == 'auto'; | |||
23 | this.create_element(); |
|
25 | this.create_element(); | |
24 | if (this.element !== null) { |
|
26 | if (this.element !== null) { | |
25 | this.element.data("cell", this); |
|
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 | Cell.prototype.typeset = function () { |
|
53 | Cell.prototype.typeset = function () { | |
54 | if (window.MathJax){ |
|
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 | Cell.prototype.select = function () { |
|
60 | Cell.prototype.select = function () { | |
61 | this.element.addClass('ui-widget-content ui-corner-all'); |
|
61 | this.element.addClass('ui-widget-content ui-corner-all'); | |
62 | this.selected = true; |
|
62 | this.selected = true; | |
@@ -154,6 +154,61 b' var IPython = (function (IPython) {' | |||||
154 | this.code_mirror.refresh(); |
|
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 | IPython.Cell = Cell; |
|
213 | IPython.Cell = Cell; | |
159 |
|
214 |
@@ -14,6 +14,7 b' var IPython = (function (IPython) {' | |||||
14 |
|
14 | |||
15 | var utils = IPython.utils; |
|
15 | var utils = IPython.utils; | |
16 | var key = IPython.utils.keycodes; |
|
16 | var key = IPython.utils.keycodes; | |
|
17 | CodeMirror.modeURL = "/static/codemirror/mode/%N/%N.js"; | |||
17 |
|
18 | |||
18 | var CodeCell = function (kernel) { |
|
19 | var CodeCell = function (kernel) { | |
19 | // The kernel doesn't have to be set at creation time, in that case |
|
20 | // The kernel doesn't have to be set at creation time, in that case | |
@@ -23,13 +24,23 b' var IPython = (function (IPython) {' | |||||
23 | this.input_prompt_number = null; |
|
24 | this.input_prompt_number = null; | |
24 | this.tooltip_on_tab = true; |
|
25 | this.tooltip_on_tab = true; | |
25 | this.collapsed = false; |
|
26 | this.collapsed = false; | |
|
27 | this.default_mode = 'python'; | |||
26 | IPython.Cell.apply(this, arguments); |
|
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 | CodeCell.prototype = new IPython.Cell(); |
|
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 | CodeCell.prototype.create_element = function () { |
|
44 | CodeCell.prototype.create_element = function () { | |
34 | var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox'); |
|
45 | var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox'); | |
35 | cell.attr('tabindex','2'); |
|
46 | cell.attr('tabindex','2'); | |
@@ -76,6 +87,9 b' var IPython = (function (IPython) {' | |||||
76 | }; |
|
87 | }; | |
77 |
|
88 | |||
78 | var cur = editor.getCursor(); |
|
89 | var cur = editor.getCursor(); | |
|
90 | if (event.keyCode === key.ENTER){ | |||
|
91 | this.auto_highlight(); | |||
|
92 | } | |||
79 |
|
93 | |||
80 | if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) { |
|
94 | if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) { | |
81 | // Always ignore shift-enter in CodeMirror as we handle it. |
|
95 | // Always ignore shift-enter in CodeMirror as we handle it. | |
@@ -109,6 +123,7 b' var IPython = (function (IPython) {' | |||||
109 | } else if (event.keyCode === key.TAB && event.type == 'keydown') { |
|
123 | } else if (event.keyCode === key.TAB && event.type == 'keydown') { | |
110 | // Tab completion. |
|
124 | // Tab completion. | |
111 | //Do not trim here because of tooltip |
|
125 | //Do not trim here because of tooltip | |
|
126 | if (editor.somethingSelected()){return false} | |||
112 | var pre_cursor = editor.getRange({line:cur.line,ch:0},cur); |
|
127 | var pre_cursor = editor.getRange({line:cur.line,ch:0},cur); | |
113 | if (pre_cursor.trim() === "") { |
|
128 | if (pre_cursor.trim() === "") { | |
114 | // Don't autocomplete if the part of the line before the cursor |
|
129 | // Don't autocomplete if the part of the line before the cursor | |
@@ -172,6 +187,7 b' var IPython = (function (IPython) {' | |||||
172 | IPython.Cell.prototype.select.apply(this); |
|
187 | IPython.Cell.prototype.select.apply(this); | |
173 | this.code_mirror.refresh(); |
|
188 | this.code_mirror.refresh(); | |
174 | this.code_mirror.focus(); |
|
189 | this.code_mirror.focus(); | |
|
190 | this.auto_highlight(); | |||
175 | // We used to need an additional refresh() after the focus, but |
|
191 | // We used to need an additional refresh() after the focus, but | |
176 | // it appears that this has been fixed in CM. This bug would show |
|
192 | // it appears that this has been fixed in CM. This bug would show | |
177 | // up on FF when a newly loaded markdown cell was edited. |
|
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 | CodeCell.prototype.set_input_prompt = function (number) { |
|
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 | this.input_prompt_number = number; |
|
251 | this.input_prompt_number = number; | |
215 | var ns = number || " "; |
|
252 | var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline); | |
216 |
this.element.find('div.input_prompt').html( |
|
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 | // make this value the starting point, so that we can only undo |
|
304 | // make this value the starting point, so that we can only undo | |
268 | // to this state, instead of a blank cell |
|
305 | // to this state, instead of a blank cell | |
269 | this.code_mirror.clearHistory(); |
|
306 | this.code_mirror.clearHistory(); | |
|
307 | this.auto_highlight(); | |||
270 | } |
|
308 | } | |
271 | if (data.prompt_number !== undefined) { |
|
309 | if (data.prompt_number !== undefined) { | |
272 | this.set_input_prompt(data.prompt_number); |
|
310 | this.set_input_prompt(data.prompt_number); |
@@ -64,7 +64,7 b' var IPython = (function (IPython) {' | |||||
64 |
|
64 | |||
65 |
|
65 | |||
66 | Kernel.prototype.restart = function () { |
|
66 | Kernel.prototype.restart = function () { | |
67 |
$([IPython.events]).trigger( |
|
67 | $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this}); | |
68 | var that = this; |
|
68 | var that = this; | |
69 | if (this.running) { |
|
69 | if (this.running) { | |
70 | this.stop_channels(); |
|
70 | this.stop_channels(); | |
@@ -86,6 +86,7 b' var IPython = (function (IPython) {' | |||||
86 | this.start_channels(); |
|
86 | this.start_channels(); | |
87 | this.shell_channel.onmessage = $.proxy(this._handle_shell_reply,this); |
|
87 | this.shell_channel.onmessage = $.proxy(this._handle_shell_reply,this); | |
88 | this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply,this); |
|
88 | this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply,this); | |
|
89 | $([IPython.events]).trigger('status_started.Kernel', {kernel: this}); | |||
89 | }; |
|
90 | }; | |
90 |
|
91 | |||
91 |
|
92 | |||
@@ -245,7 +246,8 b' var IPython = (function (IPython) {' | |||||
245 | user_expressions : {}, |
|
246 | user_expressions : {}, | |
246 | allow_stdin : false |
|
247 | allow_stdin : false | |
247 | }; |
|
248 | }; | |
248 |
|
|
249 | $.extend(true, content, options) | |
|
250 | $([IPython.events]).trigger('execution_request.Kernel', {kernel: this, content:content}); | |||
249 | var msg = this._get_msg("execute_request", content); |
|
251 | var msg = this._get_msg("execute_request", content); | |
250 | this.shell_channel.send(JSON.stringify(msg)); |
|
252 | this.shell_channel.send(JSON.stringify(msg)); | |
251 | this.set_callbacks_for_msg(msg.header.msg_id, callbacks); |
|
253 | this.set_callbacks_for_msg(msg.header.msg_id, callbacks); | |
@@ -279,7 +281,7 b' var IPython = (function (IPython) {' | |||||
279 |
|
281 | |||
280 | Kernel.prototype.interrupt = function () { |
|
282 | Kernel.prototype.interrupt = function () { | |
281 | if (this.running) { |
|
283 | if (this.running) { | |
282 |
$([IPython.events]).trigger( |
|
284 | $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this}); | |
283 | $.post(this.kernel_url + "/interrupt"); |
|
285 | $.post(this.kernel_url + "/interrupt"); | |
284 | }; |
|
286 | }; | |
285 | }; |
|
287 | }; | |
@@ -312,6 +314,7 b' var IPython = (function (IPython) {' | |||||
312 |
|
314 | |||
313 | Kernel.prototype._handle_shell_reply = function (e) { |
|
315 | Kernel.prototype._handle_shell_reply = function (e) { | |
314 | reply = $.parseJSON(e.data); |
|
316 | reply = $.parseJSON(e.data); | |
|
317 | $([IPython.events]).trigger('shell_reply.Kernel', {kernel: this, reply:reply}); | |||
315 | var header = reply.header; |
|
318 | var header = reply.header; | |
316 | var content = reply.content; |
|
319 | var content = reply.content; | |
317 | var metadata = reply.metadata; |
|
320 | var metadata = reply.metadata; | |
@@ -367,12 +370,12 b' var IPython = (function (IPython) {' | |||||
367 | } |
|
370 | } | |
368 | } else if (msg_type === 'status') { |
|
371 | } else if (msg_type === 'status') { | |
369 | if (content.execution_state === 'busy') { |
|
372 | if (content.execution_state === 'busy') { | |
370 |
$([IPython.events]).trigger( |
|
373 | $([IPython.events]).trigger('status_busy.Kernel', {kernel: this}); | |
371 | } else if (content.execution_state === 'idle') { |
|
374 | } else if (content.execution_state === 'idle') { | |
372 |
$([IPython.events]).trigger( |
|
375 | $([IPython.events]).trigger('status_idle.Kernel', {kernel: this}); | |
373 | } else if (content.execution_state === 'dead') { |
|
376 | } else if (content.execution_state === 'dead') { | |
374 | this.stop_channels(); |
|
377 | this.stop_channels(); | |
375 |
$([IPython.events]).trigger( |
|
378 | $([IPython.events]).trigger('status_dead.Kernel', {kernel: this}); | |
376 | }; |
|
379 | }; | |
377 | } else if (msg_type === 'clear_output') { |
|
380 | } else if (msg_type === 'clear_output') { | |
378 | var cb = callbacks['clear_output']; |
|
381 | var cb = callbacks['clear_output']; |
@@ -32,10 +32,10 b' var IPython = (function (IPython) {' | |||||
32 | } |
|
32 | } | |
33 | var menubar_height = $('div#menubar').outerHeight(true); |
|
33 | var menubar_height = $('div#menubar').outerHeight(true); | |
34 | var toolbar_height; |
|
34 | var toolbar_height; | |
35 | if ($('div#toolbar').css('display') === 'none') { |
|
35 | if ($('div#maintoolbar').css('display') === 'none') { | |
36 | toolbar_height = 0; |
|
36 | toolbar_height = 0; | |
37 | } else { |
|
37 | } else { | |
38 | toolbar_height = $('div#toolbar').outerHeight(true); |
|
38 | toolbar_height = $('div#maintoolbar').outerHeight(true); | |
39 | } |
|
39 | } | |
40 | return h-header_height-menubar_height-toolbar_height; // content height |
|
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 | // Distributed under the terms of the BSD License. The full license is in |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
5 | // the file COPYING, distributed as part of this software. |
|
5 | // the file COPYING, distributed as part of this software. | |
6 | //---------------------------------------------------------------------------- |
|
6 | //---------------------------------------------------------------------------- | |
7 |
|
7 | |||
8 | //============================================================================ |
|
8 | //============================================================================ | |
9 |
// MathJax |
|
9 | // MathJax utility functions | |
10 | //============================================================================ |
|
10 | //============================================================================ | |
11 |
|
11 | |||
12 | var IPython = (function (IPython) { |
|
12 | IPython.namespace('IPython.mathjaxutils'); | |
13 |
|
13 | |||
14 |
|
|
14 | IPython.mathjaxutils = (function (IPython) { | |
15 | if (window.MathJax) { |
|
15 | ||
|
16 | var init = function () { | |||
|
17 | if (window.MathJax) { | |||
16 | // MathJax loaded |
|
18 | // MathJax loaded | |
17 | MathJax.Hub.Config({ |
|
19 | MathJax.Hub.Config({ | |
18 | tex2jax: { |
|
20 | tex2jax: { | |
19 | inlineMath: [ ['$','$'], ["\\(","\\)"] ], |
|
21 | inlineMath: [ ['$','$'], ["\\(","\\)"] ], | |
20 | displayMath: [ ['$$','$$'], ["\\[","\\]"] ] |
|
22 | displayMath: [ ['$$','$$'], ["\\[","\\]"] ], | |
|
23 | processEnvironments: true | |||
21 | }, |
|
24 | }, | |
22 | displayAlign: 'left', // Change this to 'center' to center equations. |
|
25 | displayAlign: 'left', // Change this to 'center' to center equations. | |
23 | "HTML-CSS": { |
|
26 | "HTML-CSS": { | |
24 | styles: {'.MathJax_Display': {"margin": 0}} |
|
27 | styles: {'.MathJax_Display': {"margin": 0}} | |
25 | } |
|
28 | } | |
26 | }); |
|
29 | }); | |
|
30 | MathJax.Hub.Configured(); | |||
27 | } else if (window.mathjax_url != "") { |
|
31 | } else if (window.mathjax_url != "") { | |
28 | // Don't have MathJax, but should. Show dialog. |
|
32 | // Don't have MathJax, but should. Show dialog. | |
29 | var dialog = $('<div></div>') |
|
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 |
|
218 | // | |
79 | IPython.init_mathjax = init_mathjax; |
|
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 | }(IPython)); No newline at end of file |
|
241 | }(IPython)); |
@@ -128,7 +128,13 b' var IPython = (function (IPython) {' | |||||
128 | }); |
|
128 | }); | |
129 | this.element.find('#run_all_cells').click(function () { |
|
129 | this.element.find('#run_all_cells').click(function () { | |
130 | IPython.notebook.execute_all_cells(); |
|
130 | IPython.notebook.execute_all_cells(); | |
131 | }); |
|
131 | }).attr('title', 'Run all cells in the notebook'); | |
|
132 | this.element.find('#run_all_cells_above').click(function () { | |||
|
133 | IPython.notebook.execute_cells_above(); | |||
|
134 | }).attr('title', 'Run all cells above (but not including) this cell'); | |||
|
135 | this.element.find('#run_all_cells_below').click(function () { | |||
|
136 | IPython.notebook.execute_cells_below(); | |||
|
137 | }).attr('title', 'Run this cell and all cells below it'); | |||
132 | this.element.find('#to_code').click(function () { |
|
138 | this.element.find('#to_code').click(function () { | |
133 | IPython.notebook.to_code(); |
|
139 | IPython.notebook.to_code(); | |
134 | }); |
|
140 | }); |
@@ -22,6 +22,9 b' var IPython = (function (IPython) {' | |||||
22 | this.next_prompt_number = 1; |
|
22 | this.next_prompt_number = 1; | |
23 | this.kernel = null; |
|
23 | this.kernel = null; | |
24 | this.clipboard = null; |
|
24 | this.clipboard = null; | |
|
25 | this.undelete_backup = null; | |||
|
26 | this.undelete_index = null; | |||
|
27 | this.undelete_below = false; | |||
25 | this.paste_enabled = false; |
|
28 | this.paste_enabled = false; | |
26 | this.dirty = false; |
|
29 | this.dirty = false; | |
27 | this.metadata = {}; |
|
30 | this.metadata = {}; | |
@@ -139,8 +142,8 b' var IPython = (function (IPython) {' | |||||
139 | that.control_key_active = false; |
|
142 | that.control_key_active = false; | |
140 | return false; |
|
143 | return false; | |
141 | } else if (event.which === 86 && that.control_key_active) { |
|
144 | } else if (event.which === 86 && that.control_key_active) { | |
142 | // Paste selected cell = v |
|
145 | // Paste below selected cell = v | |
143 | that.paste_cell(); |
|
146 | that.paste_cell_below(); | |
144 | that.control_key_active = false; |
|
147 | that.control_key_active = false; | |
145 | return false; |
|
148 | return false; | |
146 | } else if (event.which === 68 && that.control_key_active) { |
|
149 | } else if (event.which === 68 && that.control_key_active) { | |
@@ -257,6 +260,11 b' var IPython = (function (IPython) {' | |||||
257 | IPython.quick_help.show_keyboard_shortcuts(); |
|
260 | IPython.quick_help.show_keyboard_shortcuts(); | |
258 | that.control_key_active = false; |
|
261 | that.control_key_active = false; | |
259 | return false; |
|
262 | return false; | |
|
263 | } else if (event.which === 90 && that.control_key_active) { | |||
|
264 | // Undo last cell delete = z | |||
|
265 | that.undelete(); | |||
|
266 | that.control_key_active = false; | |||
|
267 | return false; | |||
260 | } else if (that.control_key_active) { |
|
268 | } else if (that.control_key_active) { | |
261 | that.control_key_active = false; |
|
269 | that.control_key_active = false; | |
262 | return true; |
|
270 | return true; | |
@@ -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 | Notebook.prototype.scroll_to_bottom = function () { |
|
326 | Notebook.prototype.scroll_to_bottom = function () { | |
309 | this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0); |
|
327 | this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0); | |
@@ -526,13 +544,19 b' var IPython = (function (IPython) {' | |||||
526 |
|
544 | |||
527 | Notebook.prototype.delete_cell = function (index) { |
|
545 | Notebook.prototype.delete_cell = function (index) { | |
528 | var i = this.index_or_selected(index); |
|
546 | var i = this.index_or_selected(index); | |
|
547 | var cell = this.get_selected_cell(); | |||
|
548 | this.undelete_backup = cell.toJSON(); | |||
529 | if (this.is_valid_cell_index(i)) { |
|
549 | if (this.is_valid_cell_index(i)) { | |
530 | var ce = this.get_cell_element(i); |
|
550 | var ce = this.get_cell_element(i); | |
531 | ce.remove(); |
|
551 | ce.remove(); | |
532 | if (i === (this.ncells())) { |
|
552 | if (i === (this.ncells())) { | |
533 | this.select(i-1); |
|
553 | this.select(i-1); | |
|
554 | this.undelete_index = i - 1; | |||
|
555 | this.undelete_below = true; | |||
534 | } else { |
|
556 | } else { | |
535 | this.select(i); |
|
557 | this.select(i); | |
|
558 | this.undelete_index = i; | |||
|
559 | this.undelete_below = false; | |||
536 | }; |
|
560 | }; | |
537 | this.dirty = true; |
|
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 | Notebook.prototype.insert_cell_below = function (type, index) { |
|
572 | Notebook.prototype.insert_cell_below = function (type, index) { | |
544 | // type = ('code','html','markdown') |
|
573 | // type = ('code','html','markdown') | |
545 | // index = cell index or undefined to insert below selected |
|
574 | // index = cell index or undefined to insert below selected | |
546 | index = this.index_or_selected(index); |
|
575 | index = this.index_or_selected(index); | |
547 | var cell = null; |
|
576 | var cell = null; | |
|
577 | // This is intentionally < rather than <= for the sake of more | |||
|
578 | // sensible behavior in some cases. | |||
|
579 | if (this.undelete_index !== null && index < this.undelete_index) { | |||
|
580 | this.undelete_index = this.undelete_index + 1; | |||
|
581 | } | |||
548 | if (this.ncells() === 0 || this.is_valid_cell_index(index)) { |
|
582 | if (this.ncells() === 0 || this.is_valid_cell_index(index)) { | |
549 | if (type === 'code') { |
|
583 | if (type === 'code') { | |
550 | cell = new IPython.CodeCell(this.kernel); |
|
584 | cell = new IPython.CodeCell(this.kernel); | |
@@ -579,6 +613,9 b' var IPython = (function (IPython) {' | |||||
579 | // index = cell index or undefined to insert above selected |
|
613 | // index = cell index or undefined to insert above selected | |
580 | index = this.index_or_selected(index); |
|
614 | index = this.index_or_selected(index); | |
581 | var cell = null; |
|
615 | var cell = null; | |
|
616 | if (this.undelete_index !== null && index <= this.undelete_index) { | |||
|
617 | this.undelete_index = this.undelete_index + 1; | |||
|
618 | } | |||
582 | if (this.ncells() === 0 || this.is_valid_cell_index(index)) { |
|
619 | if (this.ncells() === 0 || this.is_valid_cell_index(index)) { | |
583 | if (type === 'code') { |
|
620 | if (type === 'code') { | |
584 | cell = new IPython.CodeCell(this.kernel); |
|
621 | cell = new IPython.CodeCell(this.kernel); | |
@@ -741,8 +778,8 b' var IPython = (function (IPython) {' | |||||
741 | Notebook.prototype.enable_paste = function () { |
|
778 | Notebook.prototype.enable_paste = function () { | |
742 | var that = this; |
|
779 | var that = this; | |
743 | if (!this.paste_enabled) { |
|
780 | if (!this.paste_enabled) { | |
744 | $('#paste_cell').removeClass('ui-state-disabled') |
|
781 | $('#paste_cell_replace').removeClass('ui-state-disabled') | |
745 | .on('click', function () {that.paste_cell();}); |
|
782 | .on('click', function () {that.paste_cell_replace();}); | |
746 | $('#paste_cell_above').removeClass('ui-state-disabled') |
|
783 | $('#paste_cell_above').removeClass('ui-state-disabled') | |
747 | .on('click', function () {that.paste_cell_above();}); |
|
784 | .on('click', function () {that.paste_cell_above();}); | |
748 | $('#paste_cell_below').removeClass('ui-state-disabled') |
|
785 | $('#paste_cell_below').removeClass('ui-state-disabled') | |
@@ -754,7 +791,7 b' var IPython = (function (IPython) {' | |||||
754 |
|
791 | |||
755 | Notebook.prototype.disable_paste = function () { |
|
792 | Notebook.prototype.disable_paste = function () { | |
756 | if (this.paste_enabled) { |
|
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 | $('#paste_cell_above').addClass('ui-state-disabled').off('click'); |
|
795 | $('#paste_cell_above').addClass('ui-state-disabled').off('click'); | |
759 | $('#paste_cell_below').addClass('ui-state-disabled').off('click'); |
|
796 | $('#paste_cell_below').addClass('ui-state-disabled').off('click'); | |
760 | this.paste_enabled = false; |
|
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 | if (this.clipboard !== null && this.paste_enabled) { |
|
815 | if (this.clipboard !== null && this.paste_enabled) { | |
779 | var cell_data = this.clipboard; |
|
816 | var cell_data = this.clipboard; | |
780 | var new_cell = this.insert_cell_above(cell_data.cell_type); |
|
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 | // Split/merge |
|
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 | Notebook.prototype.execute_all_cells = function () { |
|
1110 | Notebook.prototype.execute_all_cells = function () { | |
1038 |
|
|
1111 | this.execute_cell_range(0, this.ncells()); | |
1039 | for (var i=0; i<ncells; i++) { |
|
1112 | that.scroll_to_bottom(); | |
|
1113 | }; | |||
|
1114 | ||||
|
1115 | Notebook.prototype.execute_cell_range = function (start, end) { | |||
|
1116 | for (var i=start; i<end; i++) { | |||
1040 | this.select(i); |
|
1117 | this.select(i); | |
1041 | this.execute_selected_cell({add_new:false}); |
|
1118 | this.execute_selected_cell({add_new:false}); | |
1042 | }; |
|
1119 | }; | |
1043 | this.scroll_to_bottom(); |
|
|||
1044 | }; |
|
1120 | }; | |
1045 |
|
1121 | |||
1046 | // Persistance and loading |
|
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 | // Distributed under the terms of the BSD License. The full license is in |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
5 | // the file COPYING, distributed as part of this software. |
|
5 | // the file COPYING, distributed as part of this software. | |
@@ -12,7 +12,26 b'' | |||||
12 |
|
12 | |||
13 | $(document).ready(function () { |
|
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 | IPython.read_only = $('body').data('readOnly') === 'True'; |
|
36 | IPython.read_only = $('body').data('readOnly') === 'True'; | |
18 | $('div#main_app').addClass('border-box-sizing ui-widget'); |
|
37 | $('div#main_app').addClass('border-box-sizing ui-widget'); | |
@@ -29,7 +48,7 b' $(document).ready(function () {' | |||||
29 | IPython.notebook = new IPython.Notebook('div#notebook'); |
|
48 | IPython.notebook = new IPython.Notebook('div#notebook'); | |
30 | IPython.save_widget = new IPython.SaveWidget('span#save_widget'); |
|
49 | IPython.save_widget = new IPython.SaveWidget('span#save_widget'); | |
31 | IPython.menubar = new IPython.MenuBar('#menubar') |
|
50 | IPython.menubar = new IPython.MenuBar('#menubar') | |
32 | IPython.toolbar = new IPython.ToolBar('#toolbar') |
|
51 | IPython.toolbar = new IPython.MainToolBar('#maintoolbar') | |
33 | IPython.tooltip = new IPython.Tooltip() |
|
52 | IPython.tooltip = new IPython.Tooltip() | |
34 | IPython.notification_area = new IPython.NotificationArea('#notification_area') |
|
53 | IPython.notification_area = new IPython.NotificationArea('#notification_area') | |
35 | IPython.notification_area.init_notification_widgets(); |
|
54 | IPython.notification_area.init_notification_widgets(); |
@@ -379,8 +379,10 b' var IPython = (function (IPython) {' | |||||
379 | OutputArea.prototype.append_text = function (data, element, extra_class) { |
|
379 | OutputArea.prototype.append_text = function (data, element, extra_class) { | |
380 | var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_text"); |
|
380 | var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_text"); | |
381 | // escape ANSI & HTML specials in plaintext: |
|
381 | // escape ANSI & HTML specials in plaintext: | |
|
382 | data = utils.wrapUrls(data); | |||
382 | data = utils.fixConsole(data); |
|
383 | data = utils.fixConsole(data); | |
383 | data = utils.fixCarriageReturn(data); |
|
384 | data = utils.fixCarriageReturn(data); | |
|
385 | data = utils.autoLinkUrls(data); | |||
384 | if (extra_class){ |
|
386 | if (extra_class){ | |
385 | toinsert.addClass(extra_class); |
|
387 | toinsert.addClass(extra_class); | |
386 | } |
|
388 | } |
@@ -15,6 +15,7 b' var IPython = (function (IPython) {' | |||||
15 |
|
15 | |||
16 | var Pager = function (pager_selector, pager_splitter_selector) { |
|
16 | var Pager = function (pager_selector, pager_splitter_selector) { | |
17 | this.pager_element = $(pager_selector); |
|
17 | this.pager_element = $(pager_selector); | |
|
18 | this.pager_button_area = $('#pager_button_area'); | |||
18 | var that = this; |
|
19 | var that = this; | |
19 | this.percentage_height = 0.40; |
|
20 | this.percentage_height = 0.40; | |
20 | this.pager_splitter_element = $(pager_splitter_selector) |
|
21 | this.pager_splitter_element = $(pager_splitter_selector) | |
@@ -39,9 +40,24 b' var IPython = (function (IPython) {' | |||||
39 | }); |
|
40 | }); | |
40 | this.expanded = false; |
|
41 | this.expanded = false; | |
41 | this.style(); |
|
42 | this.style(); | |
|
43 | this.create_button_area(); | |||
42 | this.bind_events(); |
|
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 | Pager.prototype.style = function () { |
|
61 | Pager.prototype.style = function () { | |
46 | this.pager_splitter_element.addClass('border-box-sizing ui-widget ui-state-default'); |
|
62 | this.pager_splitter_element.addClass('border-box-sizing ui-widget ui-state-default'); | |
47 | this.pager_element.addClass('border-box-sizing ui-widget'); |
|
63 | this.pager_element.addClass('border-box-sizing ui-widget'); | |
@@ -114,6 +130,26 b' var IPython = (function (IPython) {' | |||||
114 | this.pager_element.empty(); |
|
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 | Pager.prototype.append_text = function (text) { |
|
154 | Pager.prototype.append_text = function (text) { | |
119 | var toinsert = $("<div/>").addClass("output_area output_stream"); |
|
155 | var toinsert = $("<div/>").addClass("output_area output_stream"); |
@@ -12,7 +12,7 b'' | |||||
12 |
|
12 | |||
13 | $(document).ready(function () { |
|
13 | $(document).ready(function () { | |
14 |
|
14 | |||
15 |
IPython. |
|
15 | IPython.mathjaxutils.init(); | |
16 |
|
16 | |||
17 | IPython.read_only = $('body').data('readOnly') === 'True'; |
|
17 | IPython.read_only = $('body').data('readOnly') === 'True'; | |
18 | $('div#main_app').addClass('border-box-sizing ui-widget'); |
|
18 | $('div#main_app').addClass('border-box-sizing ui-widget'); |
@@ -33,6 +33,7 b' var IPython = (function (IPython) {' | |||||
33 | {key: 'Ctrl-m c', help: 'copy cell'}, |
|
33 | {key: 'Ctrl-m c', help: 'copy cell'}, | |
34 | {key: 'Ctrl-m v', help: 'paste cell'}, |
|
34 | {key: 'Ctrl-m v', help: 'paste cell'}, | |
35 | {key: 'Ctrl-m d', help: 'delete cell'}, |
|
35 | {key: 'Ctrl-m d', help: 'delete cell'}, | |
|
36 | {key: 'Ctrl-m z', help: 'undo last cell deletion'}, | |||
36 | {key: 'Ctrl-m a', help: 'insert cell above'}, |
|
37 | {key: 'Ctrl-m a', help: 'insert cell above'}, | |
37 | {key: 'Ctrl-m b', help: 'insert cell below'}, |
|
38 | {key: 'Ctrl-m b', help: 'insert cell below'}, | |
38 | {key: 'Ctrl-m o', help: 'toggle output'}, |
|
39 | {key: 'Ctrl-m o', help: 'toggle output'}, |
@@ -1,5 +1,5 b'' | |||||
1 | //---------------------------------------------------------------------------- |
|
1 | //---------------------------------------------------------------------------- | |
2 |
// Copyright (C) 2008-201 |
|
2 | // Copyright (C) 2008-2012 The IPython Development Team | |
3 | // |
|
3 | // | |
4 | // Distributed under the terms of the BSD License. The full license is in |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
5 | // the file COPYING, distributed as part of this software. |
|
5 | // the file COPYING, distributed as part of this software. | |
@@ -221,7 +221,9 b' var IPython = (function (IPython) {' | |||||
221 | if (this.rendered === false) { |
|
221 | if (this.rendered === false) { | |
222 | var text = this.get_text(); |
|
222 | var text = this.get_text(); | |
223 | if (text === "") { text = this.placeholder; } |
|
223 | if (text === "") { text = this.placeholder; } | |
|
224 | text = IPython.mathjaxutils.remove_math(text) | |||
224 | var html = IPython.markdown_converter.makeHtml(text); |
|
225 | var html = IPython.markdown_converter.makeHtml(text); | |
|
226 | html = IPython.mathjaxutils.replace_math(html) | |||
225 | try { |
|
227 | try { | |
226 | this.set_rendered(html); |
|
228 | this.set_rendered(html); | |
227 | } catch (e) { |
|
229 | } catch (e) { | |
@@ -231,7 +233,6 b' var IPython = (function (IPython) {' | |||||
231 | "Error rendering Markdown!<br/>" + e.toString()) |
|
233 | "Error rendering Markdown!<br/>" + e.toString()) | |
232 | ); |
|
234 | ); | |
233 | } |
|
235 | } | |
234 | this.typeset() |
|
|||
235 | this.element.find('div.text_cell_input').hide(); |
|
236 | this.element.find('div.text_cell_input').hide(); | |
236 | this.element.find("div.text_cell_render").show(); |
|
237 | this.element.find("div.text_cell_render").show(); | |
237 | var code_snippets = this.element.find("pre > code"); |
|
238 | var code_snippets = this.element.find("pre > code"); | |
@@ -246,6 +247,7 b' var IPython = (function (IPython) {' | |||||
246 |
|
247 | |||
247 | return '<code class="prettyprint">' + code + '</code>'; |
|
248 | return '<code class="prettyprint">' + code + '</code>'; | |
248 | }); |
|
249 | }); | |
|
250 | this.typeset() | |||
249 | this.rendered = true; |
|
251 | this.rendered = true; | |
250 | } |
|
252 | } | |
251 | }; |
|
253 | }; | |
@@ -258,11 +260,19 b' var IPython = (function (IPython) {' | |||||
258 | this.code_mirror_mode = 'rst'; |
|
260 | this.code_mirror_mode = 'rst'; | |
259 | IPython.TextCell.apply(this, arguments); |
|
261 | IPython.TextCell.apply(this, arguments); | |
260 | this.cell_type = 'raw'; |
|
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 | RawCell.prototype = new TextCell(); |
|
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 | RawCell.prototype.render = function () { |
|
277 | RawCell.prototype.render = function () { | |
268 | this.rendered = true; |
|
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 | // Distributed under the terms of the BSD License. The full license is in |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
5 | // the file COPYING, distributed as part of this software. |
|
5 | // the file COPYING, distributed as part of this software. | |
@@ -16,132 +16,76 b' var IPython = (function (IPython) {' | |||||
16 | if (this.selector !== undefined) { |
|
16 | if (this.selector !== undefined) { | |
17 | this.element = $(selector); |
|
17 | this.element = $(selector); | |
18 | this.style(); |
|
18 | this.style(); | |
19 | this.bind_events(); |
|
|||
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 | ToolBar.prototype.style = function () { |
|
75 | ToolBar.prototype.style = function () { | |
25 | this.element.addClass('border-box-sizing'). |
|
76 | this.element.addClass('border-box-sizing'). | |
26 | addClass('ui-widget ui-widget-content'). |
|
77 | addClass('ui-widget ui-widget-content toolbar'). | |
27 | css('border-top-style','none'). |
|
78 | css('border-top-style','none'). | |
28 | css('border-left-style','none'). |
|
79 | css('border-left-style','none'). | |
29 | css('border-right-style','none'); |
|
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 | ToolBar.prototype.toggle = function () { |
|
84 | ToolBar.prototype.toggle = function () { | |
143 | this.element.toggle(); |
|
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 | // function that will be called if you press tab 1, 2, 3... times in a row |
|
104 | // function that will be called if you press tab 1, 2, 3... times in a row | |
105 | this.tabs_functions = [function (cell, text) { |
|
105 | this.tabs_functions = [function (cell, text) { | |
106 | that._request_tooltip(cell, text); |
|
106 | that._request_tooltip(cell, text); | |
107 | IPython.notification_widget.set_message('tab again to expand pager', 2500); |
|
|||
108 | }, function () { |
|
107 | }, function () { | |
109 | that.expand(); |
|
108 | that.expand(); | |
110 | IPython.notification_widget.set_message('tab again to make pager sticky for 10s', 2500); |
|
|||
111 | }, function () { |
|
109 | }, function () { | |
112 | that.stick(); |
|
110 | that.stick(); | |
113 | IPython.notification_widget.set_message('tab again to open help in pager', 2500); |
|
|||
114 | }, function (cell) { |
|
111 | }, function (cell) { | |
115 | that.cancel_stick(); |
|
112 | that.cancel_stick(); | |
116 | that.showInPager(cell); |
|
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 | // Distributed under the terms of the BSD License. The full license is in |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
5 | // the file COPYING, distributed as part of this software. |
|
5 | // the file COPYING, distributed as part of this software. | |
@@ -13,6 +13,123 b" IPython.namespace('IPython.utils');" | |||||
13 |
|
13 | |||
14 | IPython.utils = (function (IPython) { |
|
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 | var uuid = function () { |
|
133 | var uuid = function () { | |
17 | // http://www.ietf.org/rfc/rfc4122.txt |
|
134 | // http://www.ietf.org/rfc/rfc4122.txt | |
18 | var s = []; |
|
135 | var s = []; | |
@@ -78,11 +195,30 b' IPython.utils = (function (IPython) {' | |||||
78 | tmp = txt; |
|
195 | tmp = txt; | |
79 | do { |
|
196 | do { | |
80 | txt = tmp; |
|
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 | } while (tmp.length < txt.length); |
|
200 | } while (tmp.length < txt.length); | |
83 | return txt; |
|
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 | grow = function(element) { |
|
222 | grow = function(element) { | |
87 | // Grow the cell by hand. This is used upon reloading from JSON, when the |
|
223 | // Grow the cell by hand. This is used upon reloading from JSON, when the | |
88 | // autogrow handler is not called. |
|
224 | // autogrow handler is not called. | |
@@ -138,11 +274,14 b' IPython.utils = (function (IPython) {' | |||||
138 |
|
274 | |||
139 |
|
275 | |||
140 | return { |
|
276 | return { | |
|
277 | regex_split : regex_split, | |||
141 | uuid : uuid, |
|
278 | uuid : uuid, | |
142 | fixConsole : fixConsole, |
|
279 | fixConsole : fixConsole, | |
143 | keycodes : keycodes, |
|
280 | keycodes : keycodes, | |
144 | grow : grow, |
|
281 | grow : grow, | |
145 | fixCarriageReturn : fixCarriageReturn, |
|
282 | fixCarriageReturn : fixCarriageReturn, | |
|
283 | wrapUrls : wrapUrls, | |||
|
284 | autoLinkUrls : autoLinkUrls, | |||
146 | points_to_pixels : points_to_pixels |
|
285 | points_to_pixels : points_to_pixels | |
147 | }; |
|
286 | }; | |
148 |
|
287 |
@@ -3,7 +3,7 b'' | |||||
3 | {% block stylesheet %} |
|
3 | {% block stylesheet %} | |
4 |
|
4 | |||
5 | {% if mathjax_url %} |
|
5 | {% if mathjax_url %} | |
6 | <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML" charset="utf-8"></script> |
|
6 | <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script> | |
7 | {% endif %} |
|
7 | {% endif %} | |
8 | <script type="text/javascript"> |
|
8 | <script type="text/javascript"> | |
9 | // MathJax disabled, set as null to distingish from *missing* MathJax, |
|
9 | // MathJax disabled, set as null to distingish from *missing* MathJax, | |
@@ -74,9 +74,9 b' data-notebook-id={{notebook_id}}' | |||||
74 | <ul> |
|
74 | <ul> | |
75 | <li id="cut_cell"><a href="#">Cut Cell</a></li> |
|
75 | <li id="cut_cell"><a href="#">Cut Cell</a></li> | |
76 | <li id="copy_cell"><a href="#">Copy Cell</a></li> |
|
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 | <li id="paste_cell_above" class="ui-state-disabled"><a href="#">Paste Cell Above</a></li> |
|
77 | <li id="paste_cell_above" class="ui-state-disabled"><a href="#">Paste Cell Above</a></li> | |
79 | <li id="paste_cell_below" class="ui-state-disabled"><a href="#">Paste Cell Below</a></li> |
|
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 | <li id="delete_cell"><a href="#">Delete</a></li> |
|
80 | <li id="delete_cell"><a href="#">Delete</a></li> | |
81 | <hr/> |
|
81 | <hr/> | |
82 | <li id="split_cell"><a href="#">Split Cell</a></li> |
|
82 | <li id="split_cell"><a href="#">Split Cell</a></li> | |
@@ -107,6 +107,8 b' data-notebook-id={{notebook_id}}' | |||||
107 | <li id="run_cell"><a href="#">Run</a></li> |
|
107 | <li id="run_cell"><a href="#">Run</a></li> | |
108 | <li id="run_cell_in_place"><a href="#">Run in Place</a></li> |
|
108 | <li id="run_cell_in_place"><a href="#">Run in Place</a></li> | |
109 | <li id="run_all_cells"><a href="#">Run All</a></li> |
|
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 | <hr/> |
|
112 | <hr/> | |
111 | <li id="to_code"><a href="#">Code</a></li> |
|
113 | <li id="to_code"><a href="#">Code</a></li> | |
112 | <li id="to_markdown"><a href="#">Markdown </a></li> |
|
114 | <li id="to_markdown"><a href="#">Markdown </a></li> | |
@@ -156,54 +158,22 b' data-notebook-id={{notebook_id}}' | |||||
156 | </div> |
|
158 | </div> | |
157 |
|
159 | |||
158 |
|
160 | |||
159 | <div id="toolbar"> |
|
161 | <div id="maintoolbar"></div> | |
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> |
|
|||
196 |
|
162 | |||
197 | <div id="main_app"> |
|
163 | <div id="main_app"> | |
198 |
|
164 | |||
199 | <div id="notebook_panel"> |
|
165 | <div id="notebook_panel"> | |
200 | <div id="notebook"></div> |
|
166 | <div id="notebook"></div> | |
201 | <div id="pager_splitter"></div> |
|
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 | </div> |
|
173 | </div> | |
204 |
|
174 | |||
205 | </div> |
|
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 | {% endblock %} |
|
179 | {% endblock %} | |
@@ -212,6 +182,8 b' data-notebook-id={{notebook_id}}' | |||||
212 | {% block script %} |
|
182 | {% block script %} | |
213 |
|
183 | |||
214 | <script src="{{ static_url("codemirror/lib/codemirror.js") }}" charset="utf-8"></script> |
|
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 | <script src="{{ static_url("codemirror/mode/python/python.js") }}" charset="utf-8"></script> |
|
187 | <script src="{{ static_url("codemirror/mode/python/python.js") }}" charset="utf-8"></script> | |
216 | <script src="{{ static_url("codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script> |
|
188 | <script src="{{ static_url("codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script> | |
217 | <script src="{{ static_url("codemirror/mode/xml/xml.js") }}" charset="utf-8"></script> |
|
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 | <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script> |
|
200 | <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script> | |
229 | <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script> |
|
201 | <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script> | |
230 | <script src="{{ static_url("js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script> |
|
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 | <script src="{{ static_url("js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script> |
|
204 | <script src="{{ static_url("js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script> | |
233 | <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script> |
|
205 | <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script> | |
234 | <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script> |
|
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 | <script src="{{ static_url("js/pager.js") }}" type="text/javascript" charset="utf-8"></script> |
|
212 | <script src="{{ static_url("js/pager.js") }}" type="text/javascript" charset="utf-8"></script> | |
241 | <script src="{{ static_url("js/menubar.js") }}" type="text/javascript" charset="utf-8"></script> |
|
213 | <script src="{{ static_url("js/menubar.js") }}" type="text/javascript" charset="utf-8"></script> | |
242 | <script src="{{ static_url("js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script> |
|
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 | <script src="{{ static_url("js/notebook.js") }}" type="text/javascript" charset="utf-8"></script> |
|
216 | <script src="{{ static_url("js/notebook.js") }}" type="text/javascript" charset="utf-8"></script> | |
244 | <script src="{{ static_url("js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script> |
|
217 | <script src="{{ static_url("js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script> | |
245 | <script src="{{ static_url("js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script> |
|
218 | <script src="{{ static_url("js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script> | |
246 | <script src="{{ static_url("js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script> |
|
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 | <script src="{{ static_url("js/notebookmain.js") }}" type="text/javascript" charset="utf-8"></script> |
|
221 | <script src="{{ static_url("js/notebookmain.js") }}" type="text/javascript" charset="utf-8"></script> | |
248 |
|
222 | |||
249 | <script src="{{ static_url("js/contexthint.js") }}" charset="utf-8"></script> |
|
223 | <script src="{{ static_url("js/contexthint.js") }}" charset="utf-8"></script> |
@@ -28,7 +28,7 b'' | |||||
28 | <body {% block params %}{% endblock %}> |
|
28 | <body {% block params %}{% endblock %}> | |
29 |
|
29 | |||
30 | <div id="header"> |
|
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 | {% block login_widget %} |
|
33 | {% block login_widget %} | |
34 |
|
34 |
@@ -3,7 +3,7 b'' | |||||
3 | {% block stylesheet %} |
|
3 | {% block stylesheet %} | |
4 |
|
4 | |||
5 | {% if mathjax_url %} |
|
5 | {% if mathjax_url %} | |
6 | <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML" charset="utf-8"></script> |
|
6 | <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script> | |
7 | {% endif %} |
|
7 | {% endif %} | |
8 | <script type="text/javascript"> |
|
8 | <script type="text/javascript"> | |
9 | // MathJax disabled, set as null to distingish from *missing* MathJax, |
|
9 | // MathJax disabled, set as null to distingish from *missing* MathJax, | |
@@ -69,7 +69,7 b' data-notebook-id={{notebook_id}}' | |||||
69 |
|
69 | |||
70 | <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script> |
|
70 | <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script> | |
71 | <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script> |
|
71 | <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script> | |
72 |
<script src="{{ static_url("js/ |
|
72 | <script src="{{ static_url("js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script> | |
73 | <script src="{{ static_url("js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script> |
|
73 | <script src="{{ static_url("js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script> | |
74 | <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script> |
|
74 | <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script> | |
75 | <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script> |
|
75 | <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script> |
@@ -10,6 +10,7 b' import re' | |||||
10 | import sys |
|
10 | import sys | |
11 | from textwrap import dedent |
|
11 | from textwrap import dedent | |
12 | from unicodedata import category |
|
12 | from unicodedata import category | |
|
13 | import webbrowser | |||
13 |
|
14 | |||
14 | # System library imports |
|
15 | # System library imports | |
15 | from IPython.external.qt import QtCore, QtGui |
|
16 | from IPython.external.qt import QtCore, QtGui | |
@@ -267,7 +268,6 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||||
267 | self._continuation_prompt = '> ' |
|
268 | self._continuation_prompt = '> ' | |
268 | self._continuation_prompt_html = None |
|
269 | self._continuation_prompt_html = None | |
269 | self._executing = False |
|
270 | self._executing = False | |
270 | self._filter_drag = False |
|
|||
271 | self._filter_resize = False |
|
271 | self._filter_resize = False | |
272 | self._html_exporter = HtmlExporter(self._control) |
|
272 | self._html_exporter = HtmlExporter(self._control) | |
273 | self._input_buffer_executing = '' |
|
273 | self._input_buffer_executing = '' | |
@@ -342,7 +342,51 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||||
342 | triggered=self.reset_font) |
|
342 | triggered=self.reset_font) | |
343 | self.addAction(self.reset_font_size) |
|
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 | def eventFilter(self, obj, event): |
|
391 | def eventFilter(self, obj, event): | |
348 | """ Reimplemented to ensure a console-like behavior in the underlying |
|
392 | """ Reimplemented to ensure a console-like behavior in the underlying | |
@@ -391,39 +435,6 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||||
391 | event.key() in self._shortcuts: |
|
435 | event.key() in self._shortcuts: | |
392 | event.accept() |
|
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 | # Handle scrolling of the vsplit pager. This hack attempts to solve |
|
438 | # Handle scrolling of the vsplit pager. This hack attempts to solve | |
428 | # problems with tearing of the help text inside the pager window. This |
|
439 | # problems with tearing of the help text inside the pager window. This | |
429 | # happens only on Mac OS X with both PySide and PyQt. This fix isn't |
|
440 | # happens only on Mac OS X with both PySide and PyQt. This fix isn't | |
@@ -432,6 +443,11 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||||
432 | obj == self._page_control: |
|
443 | obj == self._page_control: | |
433 | self._page_control.repaint() |
|
444 | self._page_control.repaint() | |
434 | return True |
|
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 | return super(ConsoleWidget, self).eventFilter(obj, event) |
|
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 | self.layout().currentWidget().copy() |
|
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 | def cut(self): |
|
535 | def cut(self): | |
515 | """ Copy the currently selected text to the clipboard and delete it |
|
536 | """ Copy the currently selected text to the clipboard and delete it | |
516 | if it's inside the input buffer. |
|
537 | if it's inside the input buffer. | |
@@ -678,6 +699,11 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||||
678 |
|
699 | |||
679 | font = property(_get_font, _set_font) |
|
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 | def paste(self, mode=QtGui.QClipboard.Clipboard): |
|
707 | def paste(self, mode=QtGui.QClipboard.Clipboard): | |
682 | """ Paste the contents of the clipboard into the input region. |
|
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 | return result |
|
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 | def _append_html(self, html, before_prompt=False): |
|
892 | def _append_html(self, html, before_prompt=False): | |
862 | """ Appends HTML at the end of the console buffer. |
|
893 | """ Appends HTML at the end of the console buffer. | |
863 | """ |
|
894 | """ | |
@@ -966,6 +997,14 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||||
966 | self.paste_action.setEnabled(self.can_paste()) |
|
997 | self.paste_action.setEnabled(self.can_paste()) | |
967 | self.paste_action.setShortcut(QtGui.QKeySequence.Paste) |
|
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 | menu.addSeparator() |
|
1008 | menu.addSeparator() | |
970 | menu.addAction(self.select_all_action) |
|
1009 | menu.addAction(self.select_all_action) | |
971 |
|
1010 | |||
@@ -1004,9 +1043,14 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||||
1004 | elif self.kind == 'rich': |
|
1043 | elif self.kind == 'rich': | |
1005 | control = QtGui.QTextEdit() |
|
1044 | control = QtGui.QTextEdit() | |
1006 | control.setAcceptRichText(False) |
|
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 | # Install event filters. The filter on the viewport is needed for |
|
1052 | # Install event filters. The filter on the viewport is needed for | |
1009 |
# mouse events |
|
1053 | # mouse events. | |
1010 | control.installEventFilter(self) |
|
1054 | control.installEventFilter(self) | |
1011 | control.viewport().installEventFilter(self) |
|
1055 | control.viewport().installEventFilter(self) | |
1012 |
|
1056 | |||
@@ -1545,6 +1589,13 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||||
1545 | self._continuation_prompt = self._insert_html_fetching_plain_text( |
|
1589 | self._continuation_prompt = self._insert_html_fetching_plain_text( | |
1546 | cursor, self._continuation_prompt_html) |
|
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 | def _insert_html(self, cursor, html): |
|
1599 | def _insert_html(self, cursor, html): | |
1549 | """ Inserts HTML using the specified cursor in such a way that future |
|
1600 | """ Inserts HTML using the specified cursor in such a way that future | |
1550 | formatting is unaffected. |
|
1601 | formatting is unaffected. | |
@@ -1736,6 +1787,32 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||||
1736 | else: |
|
1787 | else: | |
1737 | self._append_plain_text(text) |
|
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 | def _prompt_finished(self): |
|
1816 | def _prompt_finished(self): | |
1740 | """ Called immediately after a prompt is finished, i.e. when some input |
|
1817 | """ Called immediately after a prompt is finished, i.e. when some input | |
1741 | will be processed and a new prompt displayed. |
|
1818 | will be processed and a new prompt displayed. | |
@@ -1866,7 +1943,7 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||||
1866 | cursor.movePosition(QtGui.QTextCursor.Left, |
|
1943 | cursor.movePosition(QtGui.QTextCursor.Left, | |
1867 | QtGui.QTextCursor.KeepAnchor) |
|
1944 | QtGui.QTextCursor.KeepAnchor) | |
1868 | if cursor.selection().toPlainText() != '\n': |
|
1945 | if cursor.selection().toPlainText() != '\n': | |
1869 |
self._append_ |
|
1946 | self._append_block() | |
1870 |
|
1947 | |||
1871 | # Write the prompt. |
|
1948 | # Write the prompt. | |
1872 | self._append_plain_text(self._prompt_sep) |
|
1949 | self._append_plain_text(self._prompt_sep) |
@@ -528,7 +528,25 b' class MainWindow(QtGui.QMainWindow):' | |||||
528 | statusTip="Clear the console", |
|
528 | statusTip="Clear the console", | |
529 | triggered=self.clear_magic_active_frontend) |
|
529 | triggered=self.clear_magic_active_frontend) | |
530 | self.add_menu_action(self.view_menu, self.clear_action) |
|
530 | self.add_menu_action(self.view_menu, self.clear_action) | |
531 |
|
531 | |||
|
532 | self.pager_menu = self.view_menu.addMenu("&Pager") | |||
|
533 | ||||
|
534 | hsplit_action = QtGui.QAction(".. &Horizontal Split", | |||
|
535 | self, | |||
|
536 | triggered=lambda: self.set_paging_active_frontend('hsplit')) | |||
|
537 | ||||
|
538 | vsplit_action = QtGui.QAction(" : &Vertical Split", | |||
|
539 | self, | |||
|
540 | triggered=lambda: self.set_paging_active_frontend('vsplit')) | |||
|
541 | ||||
|
542 | inside_action = QtGui.QAction(" &Inside Pager", | |||
|
543 | self, | |||
|
544 | triggered=lambda: self.set_paging_active_frontend('inside')) | |||
|
545 | ||||
|
546 | self.pager_menu.addAction(hsplit_action) | |||
|
547 | self.pager_menu.addAction(vsplit_action) | |||
|
548 | self.pager_menu.addAction(inside_action) | |||
|
549 | ||||
532 | def init_kernel_menu(self): |
|
550 | def init_kernel_menu(self): | |
533 | self.kernel_menu = self.menuBar().addMenu("&Kernel") |
|
551 | self.kernel_menu = self.menuBar().addMenu("&Kernel") | |
534 | # Qt on OSX maps Ctrl to Cmd, and Meta to Ctrl |
|
552 | # Qt on OSX maps Ctrl to Cmd, and Meta to Ctrl | |
@@ -829,6 +847,9 b' class MainWindow(QtGui.QMainWindow):' | |||||
829 | self.maximizeAct.setEnabled(True) |
|
847 | self.maximizeAct.setEnabled(True) | |
830 | self.minimizeAct.setEnabled(True) |
|
848 | self.minimizeAct.setEnabled(True) | |
831 |
|
849 | |||
|
850 | def set_paging_active_frontend(self, paging): | |||
|
851 | self.active_frontend._set_paging(paging) | |||
|
852 | ||||
832 | def close_active_frontend(self): |
|
853 | def close_active_frontend(self): | |
833 | self.close_tab(self.active_frontend) |
|
854 | self.close_tab(self.active_frontend) | |
834 |
|
855 |
@@ -2,7 +2,7 b'' | |||||
2 | import unittest |
|
2 | import unittest | |
3 |
|
3 | |||
4 | # System library imports |
|
4 | # System library imports | |
5 | from IPython.external.qt import QtGui |
|
5 | from IPython.external.qt import QtCore, QtGui | |
6 |
|
6 | |||
7 | # Local imports |
|
7 | # Local imports | |
8 | from IPython.frontend.qt.console.console_widget import ConsoleWidget |
|
8 | from IPython.frontend.qt.console.console_widget import ConsoleWidget | |
@@ -40,3 +40,41 b' class TestConsoleWidget(unittest.TestCase):' | |||||
40 | self.assertEqual(expected_outputs[i], selection) |
|
40 | self.assertEqual(expected_outputs[i], selection) | |
41 | # clear all the text |
|
41 | # clear all the text | |
42 | cursor.insertText('') |
|
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 | import bdb |
|
20 | import bdb | |
21 | import signal |
|
21 | import signal | |
|
22 | import os | |||
22 | import sys |
|
23 | import sys | |
23 | import time |
|
24 | import time | |
|
25 | import subprocess | |||
|
26 | from io import BytesIO | |||
|
27 | import base64 | |||
24 |
|
28 | |||
25 | from Queue import Empty |
|
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 | from IPython.core.alias import AliasManager, AliasError |
|
36 | from IPython.core.alias import AliasManager, AliasError | |
28 | from IPython.core import page |
|
37 | from IPython.core import page | |
29 | from IPython.utils.warn import warn, error, fatal |
|
38 | from IPython.utils.warn import warn, error, fatal | |
30 | from IPython.utils import io |
|
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 | from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell |
|
43 | from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell | |
33 | from IPython.frontend.terminal.console.completer import ZMQCompleter |
|
44 | from IPython.frontend.terminal.console.completer import ZMQCompleter | |
@@ -36,7 +47,64 b' from IPython.frontend.terminal.console.completer import ZMQCompleter' | |||||
36 | class ZMQTerminalInteractiveShell(TerminalInteractiveShell): |
|
47 | class ZMQTerminalInteractiveShell(TerminalInteractiveShell): | |
37 | """A subclass of TerminalInteractiveShell that uses the 0MQ kernel""" |
|
48 | """A subclass of TerminalInteractiveShell that uses the 0MQ kernel""" | |
38 | _executing = False |
|
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 | def __init__(self, *args, **kwargs): |
|
108 | def __init__(self, *args, **kwargs): | |
41 | self.km = kwargs.pop('kernel_manager') |
|
109 | self.km = kwargs.pop('kernel_manager') | |
42 | self.session_id = self.km.session.session |
|
110 | self.session_id = self.km.session.session | |
@@ -163,6 +231,7 b' class ZMQTerminalInteractiveShell(TerminalInteractiveShell):' | |||||
163 | elif msg_type == 'pyout': |
|
231 | elif msg_type == 'pyout': | |
164 | self.execution_count = int(sub_msg["content"]["execution_count"]) |
|
232 | self.execution_count = int(sub_msg["content"]["execution_count"]) | |
165 | format_dict = sub_msg["content"]["data"] |
|
233 | format_dict = sub_msg["content"]["data"] | |
|
234 | self.handle_rich_data(format_dict) | |||
166 | # taken from DisplayHook.__call__: |
|
235 | # taken from DisplayHook.__call__: | |
167 | hook = self.displayhook |
|
236 | hook = self.displayhook | |
168 | hook.start_displayhook() |
|
237 | hook.start_displayhook() | |
@@ -171,6 +240,61 b' class ZMQTerminalInteractiveShell(TerminalInteractiveShell):' | |||||
171 | hook.log_output(format_dict) |
|
240 | hook.log_output(format_dict) | |
172 | hook.finish_displayhook() |
|
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 | def handle_stdin_request(self, timeout=0.1): |
|
298 | def handle_stdin_request(self, timeout=0.1): | |
175 | """ Method to capture raw_input |
|
299 | """ Method to capture raw_input | |
176 | """ |
|
300 | """ |
@@ -264,8 +264,6 b' class InteractiveShellEmbed(TerminalInteractiveShell):' | |||||
264 | self.user_ns = orig_user_ns |
|
264 | self.user_ns = orig_user_ns | |
265 | self.compile.flags = orig_compile_flags |
|
265 | self.compile.flags = orig_compile_flags | |
266 |
|
266 | |||
267 | _embedded_shell = None |
|
|||
268 |
|
||||
269 |
|
267 | |||
270 | def embed(**kwargs): |
|
268 | def embed(**kwargs): | |
271 | """Call this to embed IPython at the current point in your program. |
|
269 | """Call this to embed IPython at the current point in your program. | |
@@ -284,7 +282,7 b' def embed(**kwargs):' | |||||
284 | d = 40 |
|
282 | d = 40 | |
285 | embed |
|
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 | config argument. |
|
286 | config argument. | |
289 | """ |
|
287 | """ | |
290 | config = kwargs.get('config') |
|
288 | config = kwargs.get('config') | |
@@ -294,7 +292,5 b' def embed(**kwargs):' | |||||
294 | config = load_default_config() |
|
292 | config = load_default_config() | |
295 | config.InteractiveShellEmbed = config.TerminalInteractiveShell |
|
293 | config.InteractiveShellEmbed = config.TerminalInteractiveShell | |
296 | kwargs['config'] = config |
|
294 | kwargs['config'] = config | |
297 | global _embedded_shell |
|
295 | shell = InteractiveShellEmbed.instance(**kwargs) | |
298 | if _embedded_shell is None: |
|
296 | shell(header=header, stack_depth=2, compile_flags=compile_flags) | |
299 | _embedded_shell = InteractiveShellEmbed(**kwargs) |
|
|||
300 | _embedded_shell(header=header, stack_depth=2, compile_flags=compile_flags) |
|
@@ -357,7 +357,7 b' class TerminalInteractiveShell(InteractiveShell):' | |||||
357 | usage=None, banner1=None, banner2=None, display_banner=None): |
|
357 | usage=None, banner1=None, banner2=None, display_banner=None): | |
358 |
|
358 | |||
359 | super(TerminalInteractiveShell, self).__init__( |
|
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 | user_module=user_module, custom_exceptions=custom_exceptions |
|
361 | user_module=user_module, custom_exceptions=custom_exceptions | |
362 | ) |
|
362 | ) | |
363 | # use os.system instead of utils.process.system by default, |
|
363 | # use os.system instead of utils.process.system by default, |
@@ -5,7 +5,7 b' import subprocess' | |||||
5 | import sys |
|
5 | import sys | |
6 |
|
6 | |||
7 | from IPython.core.error import TryNext |
|
7 | from IPython.core.error import TryNext | |
8 |
|
8 | import IPython.utils.py3compat as py3compat | ||
9 |
|
9 | |||
10 | def win32_clipboard_get(): |
|
10 | def win32_clipboard_get(): | |
11 | """ Get the current clipboard's text on Windows. |
|
11 | """ Get the current clipboard's text on Windows. | |
@@ -31,6 +31,7 b' def osx_clipboard_get():' | |||||
31 | text, stderr = p.communicate() |
|
31 | text, stderr = p.communicate() | |
32 | # Text comes in with old Mac \r line endings. Change them to \n. |
|
32 | # Text comes in with old Mac \r line endings. Change them to \n. | |
33 | text = text.replace('\r', '\n') |
|
33 | text = text.replace('\r', '\n') | |
|
34 | text = py3compat.cast_unicode(text, py3compat.DEFAULT_ENCODING) | |||
34 | return text |
|
35 | return text | |
35 |
|
36 | |||
36 | def tkinter_clipboard_get(): |
|
37 | def tkinter_clipboard_get(): | |
@@ -49,6 +50,7 b' def tkinter_clipboard_get():' | |||||
49 | root.withdraw() |
|
50 | root.withdraw() | |
50 | text = root.clipboard_get() |
|
51 | text = root.clipboard_get() | |
51 | root.destroy() |
|
52 | root.destroy() | |
|
53 | text = py3compat.cast_unicode(text, py3compat.DEFAULT_ENCODING) | |||
52 | return text |
|
54 | return text | |
53 |
|
55 | |||
54 |
|
56 |
@@ -1,3 +1,6 b'' | |||||
|
1 | from __future__ import unicode_literals | |||
|
2 | ||||
|
3 | ||||
1 | """Module for interactive demos using IPython. |
|
4 | """Module for interactive demos using IPython. | |
2 |
|
5 | |||
3 | This module implements a few classes for running Python scripts interactively |
|
6 | This module implements a few classes for running Python scripts interactively | |
@@ -179,7 +182,7 b' from IPython.utils.PyColorize import Parser' | |||||
179 | from IPython.utils import io |
|
182 | from IPython.utils import io | |
180 | from IPython.utils.io import file_read, file_readlines |
|
183 | from IPython.utils.io import file_read, file_readlines | |
181 | from IPython.utils.text import marquee |
|
184 | from IPython.utils.text import marquee | |
182 |
|
185 | from IPython.utils import openpy | ||
183 | __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError'] |
|
186 | __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError'] | |
184 |
|
187 | |||
185 | class DemoError(Exception): pass |
|
188 | class DemoError(Exception): pass | |
@@ -264,13 +267,13 b' class Demo(object):' | |||||
264 | self.fobj = self.src |
|
267 | self.fobj = self.src | |
265 | else: |
|
268 | else: | |
266 | # Assume it's a string or something that can be converted to one |
|
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 | def reload(self): |
|
272 | def reload(self): | |
270 | """Reload source from disk and initialize state.""" |
|
273 | """Reload source from disk and initialize state.""" | |
271 | self.fload() |
|
274 | self.fload() | |
272 |
|
275 | |||
273 |
self.src = self.fobj |
|
276 | self.src = "".join(openpy.strip_encoding_cookie(self.fobj)) | |
274 | src_b = [b.strip() for b in self.re_stop.split(self.src) if b] |
|
277 | src_b = [b.strip() for b in self.re_stop.split(self.src) if b] | |
275 | self._silent = [bool(self.re_silent.findall(b)) for b in src_b] |
|
278 | self._silent = [bool(self.re_silent.findall(b)) for b in src_b] | |
276 | self._auto = [bool(self.re_auto.findall(b)) for b in src_b] |
|
279 | self._auto = [bool(self.re_auto.findall(b)) for b in src_b] |
@@ -1,7 +1,8 b'' | |||||
1 | """Various display related classes. |
|
1 | """Various display related classes. | |
2 |
|
2 | |||
3 | Authors : MinRK |
|
3 | Authors : MinRK, dannystaple | |
4 | """ |
|
4 | """ | |
|
5 | import urllib | |||
5 |
|
6 | |||
6 | class YouTubeVideo(object): |
|
7 | class YouTubeVideo(object): | |
7 | """Class for embedding a YouTube Video in an IPython session, based on its video id. |
|
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 | vid = YouTubeVideo("foo") |
|
16 | vid = YouTubeVideo("foo") | |
16 | display(vid) |
|
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 | self.id = id |
|
32 | self.id = id | |
21 | self.width = width |
|
33 | self.width = width | |
22 | self.height = height |
|
34 | self.height = height | |
|
35 | self.params = kwargs | |||
23 |
|
36 | |||
24 | def _repr_html_(self): |
|
37 | def _repr_html_(self): | |
25 | """return YouTube embed iframe for this video id""" |
|
38 | """return YouTube embed iframe for this video id""" | |
|
39 | if self.params: | |||
|
40 | params = "?" + urllib.urlencode(self.params) | |||
|
41 | else: | |||
|
42 | params = "" | |||
26 | return """ |
|
43 | return """ | |
27 | <iframe |
|
44 | <iframe | |
28 | width="%i" |
|
45 | width="%i" | |
29 | height="%i" |
|
46 | height="%i" | |
30 | src="http://www.youtube.com/embed/%s" |
|
47 | src="http://www.youtube.com/embed/%s%s" | |
31 | frameborder="0" |
|
48 | frameborder="0" | |
32 | allowfullscreen |
|
49 | allowfullscreen | |
33 | ></iframe> |
|
50 | ></iframe> | |
34 | """%(self.width, self.height, self.id) |
|
51 | """ % (self.width, self.height, self.id, params) | |
35 |
|
@@ -85,11 +85,33 b' def create_inputhook_qt4(mgr, app=None):' | |||||
85 | return 0 |
|
85 | return 0 | |
86 | app.processEvents(QtCore.QEventLoop.AllEvents, 300) |
|
86 | app.processEvents(QtCore.QEventLoop.AllEvents, 300) | |
87 | if not stdin_ready(): |
|
87 | if not stdin_ready(): | |
|
88 | # Generally a program would run QCoreApplication::exec() | |||
|
89 | # from main() to enter and process the Qt event loop until | |||
|
90 | # quit() or exit() is called and the program terminates. | |||
|
91 | # | |||
|
92 | # For our input hook integration, we need to repeatedly | |||
|
93 | # enter and process the Qt event loop for only a short | |||
|
94 | # amount of time (say 50ms) to ensure that Python stays | |||
|
95 | # responsive to other user inputs. | |||
|
96 | # | |||
|
97 | # A naive approach would be to repeatedly call | |||
|
98 | # QCoreApplication::exec(), using a timer to quit after a | |||
|
99 | # short amount of time. Unfortunately, QCoreApplication | |||
|
100 | # emits an aboutToQuit signal before stopping, which has | |||
|
101 | # the undesirable effect of closing all modal windows. | |||
|
102 | # | |||
|
103 | # To work around this problem, we instead create a | |||
|
104 | # QEventLoop and call QEventLoop::exec(). Other than | |||
|
105 | # setting some state variables which do not seem to be | |||
|
106 | # used anywhere, the only thing QCoreApplication adds is | |||
|
107 | # the aboutToQuit signal which is precisely what we are | |||
|
108 | # trying to avoid. | |||
88 | timer = QtCore.QTimer() |
|
109 | timer = QtCore.QTimer() | |
89 | timer.timeout.connect(app.quit) |
|
110 | event_loop = QtCore.QEventLoop() | |
|
111 | timer.timeout.connect(event_loop.quit) | |||
90 | while not stdin_ready(): |
|
112 | while not stdin_ready(): | |
91 | timer.start(50) |
|
113 | timer.start(50) | |
92 |
|
|
114 | event_loop.exec_() | |
93 | timer.stop() |
|
115 | timer.stop() | |
94 | except KeyboardInterrupt: |
|
116 | except KeyboardInterrupt: | |
95 | ignore_CTRL_C() |
|
117 | ignore_CTRL_C() |
@@ -169,8 +169,13 b' class BaseParallelApplication(BaseIPythonApplication):' | |||||
169 | log_dir = self.profile_dir.log_dir |
|
169 | log_dir = self.profile_dir.log_dir | |
170 | if self.clean_logs: |
|
170 | if self.clean_logs: | |
171 | for f in os.listdir(log_dir): |
|
171 | for f in os.listdir(log_dir): | |
172 | if re.match(r'%s-\d+\.(log|err|out)'%self.name,f): |
|
172 | if re.match(r'%s-\d+\.(log|err|out)' % self.name, f): | |
173 | os.remove(os.path.join(log_dir, 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 | if self.log_to_file: |
|
179 | if self.log_to_file: | |
175 | # Start logging to the new log file |
|
180 | # Start logging to the new log file | |
176 | log_filename = self.name + u'-' + str(os.getpid()) + u'.log' |
|
181 | log_filename = self.name + u'-' + str(os.getpid()) + u'.log' |
@@ -41,6 +41,7 b' from IPython.zmq.ipkernel import Kernel, IPKernelApp' | |||||
41 | from IPython.zmq.session import ( |
|
41 | from IPython.zmq.session import ( | |
42 | Session, session_aliases, session_flags |
|
42 | Session, session_aliases, session_flags | |
43 | ) |
|
43 | ) | |
|
44 | from IPython.zmq.zmqshell import ZMQInteractiveShell | |||
44 |
|
45 | |||
45 | from IPython.config.configurable import Configurable |
|
46 | from IPython.config.configurable import Configurable | |
46 |
|
47 | |||
@@ -143,7 +144,7 b' class IPEngineApp(BaseParallelApplication):' | |||||
143 | description = _description |
|
144 | description = _description | |
144 | examples = _examples |
|
145 | examples = _examples | |
145 | config_file_name = Unicode(default_config_file_name) |
|
146 | config_file_name = Unicode(default_config_file_name) | |
146 | classes = List([ProfileDir, Session, EngineFactory, Kernel, MPI]) |
|
147 | classes = List([ZMQInteractiveShell, ProfileDir, Session, EngineFactory, Kernel, MPI]) | |
147 |
|
148 | |||
148 | startup_script = Unicode(u'', config=True, |
|
149 | startup_script = Unicode(u'', config=True, | |
149 | help='specify a script to be run at startup') |
|
150 | help='specify a script to be run at startup') |
@@ -650,18 +650,27 b' class SSHClusterLauncher(SSHLauncher):' | |||||
650 |
|
650 | |||
651 | If not specified, use calling profile, stripping out possible leading homedir. |
|
651 | If not specified, use calling profile, stripping out possible leading homedir. | |
652 | """) |
|
652 | """) | |
653 |
|
653 | |||
654 |
def |
|
654 | def _profile_dir_changed(self, name, old, new): | |
655 | """turns /home/you/.ipython/profile_foo into .ipython/profile_foo |
|
655 | if not self.remote_profile_dir: | |
656 | """ |
|
656 | # trigger remote_profile_dir_default logic again, | |
|
657 | # in case it was already triggered before profile_dir was set | |||
|
658 | self.remote_profile_dir = self._strip_home(new) | |||
|
659 | ||||
|
660 | @staticmethod | |||
|
661 | def _strip_home(path): | |||
|
662 | """turns /home/you/.ipython/profile_foo into .ipython/profile_foo""" | |||
657 | home = get_home_dir() |
|
663 | home = get_home_dir() | |
658 | if not home.endswith('/'): |
|
664 | if not home.endswith('/'): | |
659 | home = home+'/' |
|
665 | home = home+'/' | |
660 |
|
666 | |||
661 |
if |
|
667 | if path.startswith(home): | |
662 |
return |
|
668 | return path[len(home):] | |
663 | else: |
|
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 | def _cluster_id_changed(self, name, old, new): |
|
675 | def _cluster_id_changed(self, name, old, new): | |
667 | if new: |
|
676 | if new: |
@@ -418,7 +418,7 b' class Client(HasTraits):' | |||||
418 | location = cfg.setdefault('location', None) |
|
418 | location = cfg.setdefault('location', None) | |
419 |
|
419 | |||
420 | proto,addr = cfg['interface'].split('://') |
|
420 | proto,addr = cfg['interface'].split('://') | |
421 | addr = util.disambiguate_ip_address(addr) |
|
421 | addr = util.disambiguate_ip_address(addr, location) | |
422 | cfg['interface'] = "%s://%s" % (proto, addr) |
|
422 | cfg['interface'] = "%s://%s" % (proto, addr) | |
423 |
|
423 | |||
424 | # turn interface,port into full urls: |
|
424 | # turn interface,port into full urls: | |
@@ -705,12 +705,9 b' class Client(HasTraits):' | |||||
705 | except: |
|
705 | except: | |
706 | content = error.wrap_exception() |
|
706 | content = error.wrap_exception() | |
707 | # build a fake message: |
|
707 | # build a fake message: | |
708 | parent = {} |
|
708 | msg = self.session.msg('apply_reply', content=content) | |
709 | header = {} |
|
709 | msg['parent_header']['msg_id'] = msg_id | |
710 | parent['msg_id'] = msg_id |
|
710 | msg['metadata']['engine'] = uuid | |
711 | header['engine'] = uuid |
|
|||
712 | header['date'] = datetime.now() |
|
|||
713 | msg = dict(parent_header=parent, header=header, content=content) |
|
|||
714 | self._handle_apply_reply(msg) |
|
711 | self._handle_apply_reply(msg) | |
715 |
|
712 | |||
716 | def _handle_execute_reply(self, msg): |
|
713 | def _handle_execute_reply(self, msg): |
@@ -19,7 +19,7 b' import time' | |||||
19 | import uuid |
|
19 | import uuid | |
20 |
|
20 | |||
21 | import zmq |
|
21 | import zmq | |
22 | from zmq.devices import ThreadDevice |
|
22 | from zmq.devices import ThreadDevice, ThreadMonitoredQueue | |
23 | from zmq.eventloop import ioloop, zmqstream |
|
23 | from zmq.eventloop import ioloop, zmqstream | |
24 |
|
24 | |||
25 | from IPython.config.configurable import LoggingConfigurable |
|
25 | from IPython.config.configurable import LoggingConfigurable | |
@@ -39,8 +39,11 b' class Heart(object):' | |||||
39 | You can specify the DEALER's IDENTITY via the optional heart_id argument.""" |
|
39 | You can specify the DEALER's IDENTITY via the optional heart_id argument.""" | |
40 | device=None |
|
40 | device=None | |
41 | id=None |
|
41 | id=None | |
42 | def __init__(self, in_addr, out_addr, in_type=zmq.SUB, out_type=zmq.DEALER, heart_id=None): |
|
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 | self.device = ThreadDevice(zmq.FORWARDER, in_type, out_type) |
|
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 | # do not allow the device to share global Context.instance, |
|
47 | # do not allow the device to share global Context.instance, | |
45 | # which is the default behavior in pyzmq > 2.1.10 |
|
48 | # which is the default behavior in pyzmq > 2.1.10 | |
46 | self.device.context_factory = zmq.Context |
|
49 | self.device.context_factory = zmq.Context | |
@@ -48,6 +51,8 b' class Heart(object):' | |||||
48 | self.device.daemon=True |
|
51 | self.device.daemon=True | |
49 | self.device.connect_in(in_addr) |
|
52 | self.device.connect_in(in_addr) | |
50 | self.device.connect_out(out_addr) |
|
53 | self.device.connect_out(out_addr) | |
|
54 | if mon_addr is not None: | |||
|
55 | self.device.connect_mon(mon_addr) | |||
51 | if in_type == zmq.SUB: |
|
56 | if in_type == zmq.SUB: | |
52 | self.device.setsockopt_in(zmq.SUBSCRIBE, b"") |
|
57 | self.device.setsockopt_in(zmq.SUBSCRIBE, b"") | |
53 | if heart_id is None: |
|
58 | if heart_id is None: | |
@@ -122,7 +127,7 b' class HeartMonitor(LoggingConfigurable):' | |||||
122 | map(self.handle_heart_failure, heartfailures) |
|
127 | map(self.handle_heart_failure, heartfailures) | |
123 | self.on_probation = missed_beats.intersection(self.hearts) |
|
128 | self.on_probation = missed_beats.intersection(self.hearts) | |
124 | self.responses = set() |
|
129 | self.responses = set() | |
125 |
# |
|
130 | #print self.on_probation, self.hearts | |
126 | # self.log.debug("heartbeat::beat %.3f, %i beating hearts", self.lifetime, len(self.hearts)) |
|
131 | # self.log.debug("heartbeat::beat %.3f, %i beating hearts", self.lifetime, len(self.hearts)) | |
127 | self.pingstream.send(str_to_bytes(str(self.lifetime))) |
|
132 | self.pingstream.send(str_to_bytes(str(self.lifetime))) | |
128 | # flush stream to force immediate socket send |
|
133 | # flush stream to force immediate socket send | |
@@ -165,18 +170,3 b' class HeartMonitor(LoggingConfigurable):' | |||||
165 | else: |
|
170 | else: | |
166 | self.log.warn("heartbeat::got bad heartbeat (possibly old?): %s (current=%.3f)", msg[1], self.lifetime) |
|
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 | self.log.debug("registration::register_engine(%i, %r)", eid, uuid) |
|
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 | # check if requesting available IDs: |
|
908 | # check if requesting available IDs: | |
909 | if cast_bytes(uuid) in self.by_ident: |
|
909 | if cast_bytes(uuid) in self.by_ident: | |
910 | try: |
|
910 | try: |
@@ -25,7 +25,7 b' from zmq.eventloop import ioloop, zmqstream' | |||||
25 | from IPython.external.ssh import tunnel |
|
25 | from IPython.external.ssh import tunnel | |
26 | # internal |
|
26 | # internal | |
27 | from IPython.utils.traitlets import ( |
|
27 | from IPython.utils.traitlets import ( | |
28 |
Instance, Dict, Integer, Type, |
|
28 | Instance, Dict, Integer, Type, Float, Integer, Unicode, CBytes, Bool | |
29 | ) |
|
29 | ) | |
30 | from IPython.utils.py3compat import cast_bytes |
|
30 | from IPython.utils.py3compat import cast_bytes | |
31 |
|
31 | |||
@@ -50,9 +50,14 b' class EngineFactory(RegistrationFactory):' | |||||
50 | help="""The location (an IP address) of the controller. This is |
|
50 | help="""The location (an IP address) of the controller. This is | |
51 | used for disambiguating URLs, to determine whether |
|
51 | used for disambiguating URLs, to determine whether | |
52 | loopback should be used to connect or the public address.""") |
|
52 | loopback should be used to connect or the public address.""") | |
53 |
timeout= |
|
53 | timeout=Float(5.0, config=True, | |
54 | help="""The time (in seconds) to wait for the Controller to respond |
|
54 | help="""The time (in seconds) to wait for the Controller to respond | |
55 | to registration requests before giving up.""") |
|
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 | sshserver=Unicode(config=True, |
|
61 | sshserver=Unicode(config=True, | |
57 | help="""The SSH server to use for tunneling connections to the Controller.""") |
|
62 | help="""The SSH server to use for tunneling connections to the Controller.""") | |
58 | sshkey=Unicode(config=True, |
|
63 | sshkey=Unicode(config=True, | |
@@ -60,12 +65,23 b' class EngineFactory(RegistrationFactory):' | |||||
60 | paramiko=Bool(sys.platform == 'win32', config=True, |
|
65 | paramiko=Bool(sys.platform == 'win32', config=True, | |
61 | help="""Whether to use paramiko instead of openssh for tunnels.""") |
|
66 | help="""Whether to use paramiko instead of openssh for tunnels.""") | |
62 |
|
67 | |||
|
68 | ||||
63 | # not configurable: |
|
69 | # not configurable: | |
64 | connection_info = Dict() |
|
70 | connection_info = Dict() | |
65 | user_ns = Dict() |
|
71 | user_ns = Dict() | |
66 | id = Integer(allow_none=True) |
|
72 | id = Integer(allow_none=True) | |
67 | registrar = Instance('zmq.eventloop.zmqstream.ZMQStream') |
|
73 | registrar = Instance('zmq.eventloop.zmqstream.ZMQStream') | |
68 | kernel = Instance(Kernel) |
|
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 | bident = CBytes() |
|
86 | bident = CBytes() | |
71 | ident = Unicode() |
|
87 | ident = Unicode() | |
@@ -134,6 +150,11 b' class EngineFactory(RegistrationFactory):' | |||||
134 | # print (self.session.key) |
|
150 | # print (self.session.key) | |
135 | self.session.send(self.registrar, "registration_request", content=content) |
|
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 | def complete_registration(self, msg, connect, maybe_tunnel): |
|
158 | def complete_registration(self, msg, connect, maybe_tunnel): | |
138 | # print msg |
|
159 | # print msg | |
139 | self._abort_dc.stop() |
|
160 | self._abort_dc.stop() | |
@@ -156,8 +177,20 b' class EngineFactory(RegistrationFactory):' | |||||
156 | # possibly forward hb ports with tunnels |
|
177 | # possibly forward hb ports with tunnels | |
157 | hb_ping = maybe_tunnel(url('hb_ping')) |
|
178 | hb_ping = maybe_tunnel(url('hb_ping')) | |
158 | hb_pong = maybe_tunnel(url('hb_pong')) |
|
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 | heart.start() |
|
194 | heart.start() | |
162 |
|
195 | |||
163 | # create Shell Connections (MUX, Task, etc.): |
|
196 | # create Shell Connections (MUX, Task, etc.): | |
@@ -201,6 +234,20 b' class EngineFactory(RegistrationFactory):' | |||||
201 |
|
234 | |||
202 | self.kernel.shell.display_pub.topic = cast_bytes('engine.%i.displaypub' % self.id) |
|
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 | # FIXME: This is a hack until IPKernelApp and IPEngineApp can be fully merged |
|
251 | # FIXME: This is a hack until IPKernelApp and IPEngineApp can be fully merged | |
205 | app = IPKernelApp(config=self.config, shell=self.kernel.shell, kernel=self.kernel, log=self.log) |
|
252 | app = IPKernelApp(config=self.config, shell=self.kernel.shell, kernel=self.kernel, log=self.log) | |
206 | app.init_profile_dir() |
|
253 | app.init_profile_dir() | |
@@ -228,9 +275,29 b' class EngineFactory(RegistrationFactory):' | |||||
228 | time.sleep(1) |
|
275 | time.sleep(1) | |
229 | sys.exit(255) |
|
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 | def start(self): |
|
297 | def start(self): | |
232 | dc = ioloop.DelayedCallback(self.register, 0, self.loop) |
|
298 | dc = ioloop.DelayedCallback(self.register, 0, self.loop) | |
233 | dc.start() |
|
299 | dc.start() | |
234 | self._abort_dc = ioloop.DelayedCallback(self.abort, self.timeout*1000, self.loop) |
|
300 | self._abort_dc = ioloop.DelayedCallback(self.abort, self.timeout*1000, self.loop) | |
235 | self._abort_dc.start() |
|
301 | self._abort_dc.start() | |
236 |
|
302 | |||
|
303 |
@@ -21,6 +21,7 b' import time' | |||||
21 |
|
21 | |||
22 | import zmq |
|
22 | import zmq | |
23 | from nose import SkipTest |
|
23 | from nose import SkipTest | |
|
24 | from nose.plugins.attrib import attr | |||
24 |
|
25 | |||
25 | from IPython import parallel as pmod |
|
26 | from IPython import parallel as pmod | |
26 | from IPython.parallel import error |
|
27 | from IPython.parallel import error | |
@@ -38,9 +39,9 b' class TestLoadBalancedView(ClusterTestCase):' | |||||
38 | ClusterTestCase.setUp(self) |
|
39 | ClusterTestCase.setUp(self) | |
39 | self.view = self.client.load_balanced_view() |
|
40 | self.view = self.client.load_balanced_view() | |
40 |
|
41 | |||
|
42 | @attr('crash') | |||
41 | def test_z_crash_task(self): |
|
43 | def test_z_crash_task(self): | |
42 | """test graceful handling of engine death (balanced)""" |
|
44 | """test graceful handling of engine death (balanced)""" | |
43 | raise SkipTest("crash tests disabled, due to undesirable crash reports") |
|
|||
44 | # self.add_engines(1) |
|
45 | # self.add_engines(1) | |
45 | ar = self.view.apply_async(crash) |
|
46 | ar = self.view.apply_async(crash) | |
46 | self.assertRaisesRemote(error.EngineError, ar.get, 10) |
|
47 | self.assertRaisesRemote(error.EngineError, ar.get, 10) |
@@ -24,6 +24,7 b' from StringIO import StringIO' | |||||
24 |
|
24 | |||
25 | import zmq |
|
25 | import zmq | |
26 | from nose import SkipTest |
|
26 | from nose import SkipTest | |
|
27 | from nose.plugins.attrib import attr | |||
27 |
|
28 | |||
28 | from IPython.testing import decorators as dec |
|
29 | from IPython.testing import decorators as dec | |
29 | from IPython.testing.ipunittest import ParametricTestCase |
|
30 | from IPython.testing.ipunittest import ParametricTestCase | |
@@ -51,9 +52,9 b' class TestView(ClusterTestCase, ParametricTestCase):' | |||||
51 | time.sleep(2) |
|
52 | time.sleep(2) | |
52 | super(TestView, self).setUp() |
|
53 | super(TestView, self).setUp() | |
53 |
|
54 | |||
|
55 | @attr('crash') | |||
54 | def test_z_crash_mux(self): |
|
56 | def test_z_crash_mux(self): | |
55 | """test graceful handling of engine death (direct)""" |
|
57 | """test graceful handling of engine death (direct)""" | |
56 | raise SkipTest("crash tests disabled, due to undesirable crash reports") |
|
|||
57 | # self.add_engines(1) |
|
58 | # self.add_engines(1) | |
58 | eid = self.client.ids[-1] |
|
59 | eid = self.client.ids[-1] | |
59 | ar = self.client[eid].apply_async(crash) |
|
60 | ar = self.client[eid].apply_async(crash) | |
@@ -700,4 +701,22 b' class TestView(ClusterTestCase, ParametricTestCase):' | |||||
700 | drank = amr.get(5) |
|
701 | drank = amr.get(5) | |
701 | self.assertEqual(drank, [ r*2 for r in ranks ]) |
|
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 | @interactive |
|
229 | @interactive | |
230 | def _push(**ns): |
|
230 | def _push(**ns): | |
231 | """helper method for implementing `client.push` via `client.apply`""" |
|
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 | @interactive |
|
243 | @interactive | |
235 | def _pull(keys): |
|
244 | def _pull(keys): | |
236 | """helper method for implementing `client.pull` via `client.apply`""" |
|
245 | """helper method for implementing `client.pull` via `client.apply`""" | |
237 | user_ns = globals() |
|
|||
238 | if isinstance(keys, (list,tuple, set)): |
|
246 | if isinstance(keys, (list,tuple, set)): | |
239 | for key in keys: |
|
247 | return map(lambda key: eval(key, globals()), keys) | |
240 | if key not in user_ns: |
|
|||
241 | raise NameError("name '%s' is not defined"%key) |
|
|||
242 | return map(user_ns.get, keys) |
|
|||
243 | else: |
|
248 | else: | |
244 | if keys not in user_ns: |
|
249 | return eval(keys, globals()) | |
245 | raise NameError("name '%s' is not defined"%keys) |
|
|||
246 | return user_ns.get(keys) |
|
|||
247 |
|
250 | |||
248 | @interactive |
|
251 | @interactive | |
249 | def _execute(code): |
|
252 | def _execute(code): |
@@ -396,6 +396,8 b' class IPTester(object):' | |||||
396 | """Run the stored commands""" |
|
396 | """Run the stored commands""" | |
397 | try: |
|
397 | try: | |
398 | retcode = self._run_cmd() |
|
398 | retcode = self._run_cmd() | |
|
399 | except KeyboardInterrupt: | |||
|
400 | return -signal.SIGINT | |||
399 | except: |
|
401 | except: | |
400 | import traceback |
|
402 | import traceback | |
401 | traceback.print_exc() |
|
403 | traceback.print_exc() | |
@@ -412,17 +414,22 b' class IPTester(object):' | |||||
412 | continue # process is already dead |
|
414 | continue # process is already dead | |
413 |
|
415 | |||
414 | try: |
|
416 | try: | |
415 | print('Cleaning stale PID: %d' % subp.pid) |
|
417 | print('Cleaning up stale PID: %d' % subp.pid) | |
416 | subp.kill() |
|
418 | subp.kill() | |
417 | except: # (OSError, WindowsError) ? |
|
419 | except: # (OSError, WindowsError) ? | |
418 | # This is just a best effort, if we fail or the process was |
|
420 | # This is just a best effort, if we fail or the process was | |
419 | # really gone, ignore it. |
|
421 | # really gone, ignore it. | |
420 | pass |
|
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 | if subp.poll() is None: |
|
430 | if subp.poll() is None: | |
423 | # The process did not die... |
|
431 | # The process did not die... | |
424 | print('... failed. Manual cleanup may be required.' |
|
432 | print('... failed. Manual cleanup may be required.') | |
425 | % subp.pid) |
|
|||
426 |
|
433 | |||
427 | def make_runners(inc_slow=False): |
|
434 | def make_runners(inc_slow=False): | |
428 | """Define the top-level packages that need to be tested. |
|
435 | """Define the top-level packages that need to be tested. | |
@@ -476,6 +483,8 b' def run_iptest():' | |||||
476 | # setuptools devs refuse to fix this problem! |
|
483 | # setuptools devs refuse to fix this problem! | |
477 | '--exe', |
|
484 | '--exe', | |
478 | ] |
|
485 | ] | |
|
486 | if '-a' not in argv and '-A' not in argv: | |||
|
487 | argv = argv + ['-a', '!crash'] | |||
479 |
|
488 | |||
480 | if nose.__version__ >= '0.11': |
|
489 | if nose.__version__ >= '0.11': | |
481 | # I don't fully understand why we need this one, but depending on what |
|
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 | res = runner.run() |
|
542 | res = runner.run() | |
534 | if res: |
|
543 | if res: | |
535 | failed.append( (name, runner) ) |
|
544 | failed.append( (name, runner) ) | |
|
545 | if res == -signal.SIGINT: | |||
|
546 | print("Interrupted") | |||
|
547 | break | |||
536 | finally: |
|
548 | finally: | |
537 | os.chdir(curdir) |
|
549 | os.chdir(curdir) | |
538 | t_end = time.time() |
|
550 | t_end = time.time() |
@@ -30,6 +30,8 b' formatting (which is the hard part).' | |||||
30 | """ |
|
30 | """ | |
31 | from __future__ import print_function |
|
31 | from __future__ import print_function | |
32 |
|
32 | |||
|
33 | from __future__ import unicode_literals | |||
|
34 | ||||
33 | __all__ = ['ANSICodeColors','Parser'] |
|
35 | __all__ = ['ANSICodeColors','Parser'] | |
34 |
|
36 | |||
35 | _scheme_default = 'Linux' |
|
37 | _scheme_default = 'Linux' |
@@ -7,7 +7,7 b' Much of the code is taken from the tokenize module in Python 3.2.' | |||||
7 | from __future__ import absolute_import |
|
7 | from __future__ import absolute_import | |
8 |
|
8 | |||
9 | import io |
|
9 | import io | |
10 | from io import TextIOWrapper |
|
10 | from io import TextIOWrapper, BytesIO | |
11 | import re |
|
11 | import re | |
12 | import urllib |
|
12 | import urllib | |
13 |
|
13 | |||
@@ -120,6 +120,32 b' except ImportError:' | |||||
120 | text.mode = 'r' |
|
120 | text.mode = 'r' | |
121 | return text |
|
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 | def strip_encoding_cookie(filelike): |
|
149 | def strip_encoding_cookie(filelike): | |
124 | """Generator to pull lines from a text-mode file, skipping the encoding |
|
150 | """Generator to pull lines from a text-mode file, skipping the encoding | |
125 | cookie if it is found in the first two lines. |
|
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 | response = urllib.urlopen(url) |
|
208 | response = urllib.urlopen(url) | |
183 | buffer = io.BytesIO(response.read()) |
|
209 | buffer = io.BytesIO(response.read()) | |
184 | encoding, lines = detect_encoding(buffer.readline) |
|
210 | return source_to_unicode(buffer, errors, skip_encoding_cookie) | |
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() |
|
|||
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 | import tempfile |
|
19 | import tempfile | |
20 | import warnings |
|
20 | import warnings | |
21 | from hashlib import md5 |
|
21 | from hashlib import md5 | |
|
22 | import glob | |||
22 |
|
23 | |||
23 | import IPython |
|
24 | import IPython | |
24 | from IPython.testing.skipdoctest import skip_doctest |
|
25 | from IPython.testing.skipdoctest import skip_doctest | |
@@ -355,6 +356,31 b' def expand_path(s):' | |||||
355 | return s |
|
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 | def target_outdated(target,deps): |
|
384 | def target_outdated(target,deps): | |
359 | """Determine whether a target is out of date. |
|
385 | """Determine whether a target is out of date. | |
360 |
|
386 |
@@ -98,12 +98,8 b' class CannedFunction(CannedObject):' | |||||
98 | def get_object(self, g=None): |
|
98 | def get_object(self, g=None): | |
99 | # try to load function back into its module: |
|
99 | # try to load function back into its module: | |
100 | if not self.module.startswith('__'): |
|
100 | if not self.module.startswith('__'): | |
101 | try: |
|
101 | __import__(self.module) | |
102 |
|
|
102 | g = sys.modules[self.module].__dict__ | |
103 | except ImportError: |
|
|||
104 | pass |
|
|||
105 | else: |
|
|||
106 | g = sys.modules[self.module].__dict__ |
|
|||
107 |
|
103 | |||
108 | if g is None: |
|
104 | if g is None: | |
109 | g = {} |
|
105 | g = {} |
@@ -73,7 +73,7 b" if have_readline and hasattr(_rl, 'rlmain'):" | |||||
73 | line = lineobj.TextLine(line) |
|
73 | line = lineobj.TextLine(line) | |
74 | return _rl.add_history(line) |
|
74 | return _rl.add_history(line) | |
75 |
|
75 | |||
76 | if sys.platform == 'win32' and have_readline: |
|
76 | if (sys.platform == 'win32' or sys.platform == 'cli') and have_readline: | |
77 | try: |
|
77 | try: | |
78 | _outputfile=_rl.GetOutputFile() |
|
78 | _outputfile=_rl.GetOutputFile() | |
79 | except AttributeError: |
|
79 | except AttributeError: |
@@ -3,14 +3,14 b'' | |||||
3 | This is copied from the stdlib and will be standard in Python 3.2 and onwards. |
|
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 | # This code should only be used in Python versions < 3.2, since after that we |
|
8 | # This code should only be used in Python versions < 3.2, since after that we | |
7 | # can rely on the stdlib itself. |
|
9 | # can rely on the stdlib itself. | |
8 | try: |
|
10 | try: | |
9 | from tempfile import TemporaryDirectory |
|
11 | from tempfile import TemporaryDirectory | |
10 |
|
12 | |||
11 | except ImportError: |
|
13 | except ImportError: | |
12 |
|
||||
13 | import os as _os |
|
|||
14 | from tempfile import mkdtemp, template |
|
14 | from tempfile import mkdtemp, template | |
15 |
|
15 | |||
16 | class TemporaryDirectory(object): |
|
16 | class TemporaryDirectory(object): | |
@@ -74,3 +74,33 b' except ImportError:' | |||||
74 | self._rmdir(path) |
|
74 | self._rmdir(path) | |
75 | except self._os_error: |
|
75 | except self._os_error: | |
76 | pass |
|
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 | if os.name == 'posix': |
|
82 | if os.name == 'posix': | |
83 | TERM = os.environ.get('TERM','') |
|
83 | TERM = os.environ.get('TERM','') | |
84 | if (TERM == 'xterm') or (TERM == 'xterm-color'): |
|
84 | if TERM.startswith('xterm'): | |
85 | _set_term_title = _set_term_title_xterm |
|
85 | _set_term_title = _set_term_title_xterm | |
86 |
|
86 | |||
87 |
|
87 |
@@ -19,6 +19,7 b' import shutil' | |||||
19 | import sys |
|
19 | import sys | |
20 | import tempfile |
|
20 | import tempfile | |
21 | from io import StringIO |
|
21 | from io import StringIO | |
|
22 | from contextlib import contextmanager | |||
22 |
|
23 | |||
23 | from os.path import join, abspath, split |
|
24 | from os.path import join, abspath, split | |
24 |
|
25 | |||
@@ -32,6 +33,7 b' from IPython.testing.decorators import skip_if_not_win32, skip_win32' | |||||
32 | from IPython.testing.tools import make_tempfile, AssertPrints |
|
33 | from IPython.testing.tools import make_tempfile, AssertPrints | |
33 | from IPython.utils import path, io |
|
34 | from IPython.utils import path, io | |
34 | from IPython.utils import py3compat |
|
35 | from IPython.utils import py3compat | |
|
36 | from IPython.utils.tempdir import TemporaryDirectory | |||
35 |
|
37 | |||
36 | # Platform-dependent imports |
|
38 | # Platform-dependent imports | |
37 | try: |
|
39 | try: | |
@@ -444,3 +446,79 b' def test_unicode_in_filename():' | |||||
444 | path.get_py_filename(u'fooéè.py', force_win32=False) |
|
446 | path.get_py_filename(u'fooéè.py', force_win32=False) | |
445 | except IOError as ex: |
|
447 | except IOError as ex: | |
446 | str(ex) |
|
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 | import sys |
|
25 | import sys | |
26 | from unittest import TestCase |
|
26 | from unittest import TestCase | |
27 |
|
27 | |||
|
28 | import nose.tools as nt | |||
28 | from nose import SkipTest |
|
29 | from nose import SkipTest | |
29 |
|
30 | |||
30 | from IPython.utils.traitlets import ( |
|
31 | from IPython.utils.traitlets import ( | |
31 | HasTraits, MetaHasTraits, TraitType, Any, CBytes, |
|
32 | HasTraits, MetaHasTraits, TraitType, Any, CBytes, Dict, | |
32 | Int, Long, Integer, Float, Complex, Bytes, Unicode, TraitError, |
|
33 | Int, Long, Integer, Float, Complex, Bytes, Unicode, TraitError, | |
33 | Undefined, Type, This, Instance, TCPAddress, List, Tuple, |
|
34 | Undefined, Type, This, Instance, TCPAddress, List, Tuple, | |
34 | ObjectName, DottedObjectName, CRegExp |
|
35 | ObjectName, DottedObjectName, CRegExp | |
@@ -906,3 +907,14 b' class TestCRegExp(TraitTestBase):' | |||||
906 | _default_value = re.compile(r'') |
|
907 | _default_value = re.compile(r'') | |
907 | _good_values = [r'\d+', re.compile(r'\d+')] |
|
908 | _good_values = [r'\d+', re.compile(r'\d+')] | |
908 | _bad_values = [r'(', None, ()] |
|
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 | return msg |
|
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 | def parse_notifier_name(name): |
|
127 | def parse_notifier_name(name): | |
121 | """Convert the name argument to a list of names. |
|
128 | """Convert the name argument to a list of names. | |
122 |
|
129 | |||
@@ -302,8 +309,8 b' class TraitType(object):' | |||||
302 | def __set__(self, obj, value): |
|
309 | def __set__(self, obj, value): | |
303 | new_value = self._validate(obj, value) |
|
310 | new_value = self._validate(obj, value) | |
304 | old_value = self.__get__(obj) |
|
311 | old_value = self.__get__(obj) | |
|
312 | obj._trait_values[self.name] = new_value | |||
305 | if old_value != new_value: |
|
313 | if old_value != new_value: | |
306 | obj._trait_values[self.name] = new_value |
|
|||
307 | obj._notify_trait(self.name, old_value, new_value) |
|
314 | obj._notify_trait(self.name, old_value, new_value) | |
308 |
|
315 | |||
309 | def _validate(self, obj, value): |
|
316 | def _validate(self, obj, value): | |
@@ -920,11 +927,15 b' else:' | |||||
920 | def validate(self, obj, value): |
|
927 | def validate(self, obj, value): | |
921 | if isinstance(value, int): |
|
928 | if isinstance(value, int): | |
922 | return value |
|
929 | return value | |
923 |
|
|
930 | if isinstance(value, long): | |
924 | # downcast longs that fit in int: |
|
931 | # downcast longs that fit in int: | |
925 | # note that int(n > sys.maxint) returns a long, so |
|
932 | # note that int(n > sys.maxint) returns a long, so | |
926 | # we don't need a condition on this cast |
|
933 | # we don't need a condition on this cast | |
927 | return int(value) |
|
934 | return int(value) | |
|
935 | if sys.platform == "cli": | |||
|
936 | from System import Int64 | |||
|
937 | if isinstance(value, Int64): | |||
|
938 | return int(value) | |||
928 | self.error(obj, value) |
|
939 | self.error(obj, value) | |
929 |
|
940 | |||
930 |
|
941 | |||
@@ -1165,10 +1176,8 b' class Container(Instance):' | |||||
1165 | further keys for extensions to the Trait (e.g. config) |
|
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 | # allow List([values]): |
|
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 | default_value = trait |
|
1181 | default_value = trait | |
1173 | trait = None |
|
1182 | trait = None | |
1174 |
|
1183 | |||
@@ -1179,8 +1188,8 b' class Container(Instance):' | |||||
1179 | else: |
|
1188 | else: | |
1180 | raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value)) |
|
1189 | raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value)) | |
1181 |
|
1190 | |||
1182 | if istrait(trait): |
|
1191 | if is_trait(trait): | |
1183 | self._trait = trait() |
|
1192 | self._trait = trait() if isinstance(trait, type) else trait | |
1184 | self._trait.name = 'element' |
|
1193 | self._trait.name = 'element' | |
1185 | elif trait is not None: |
|
1194 | elif trait is not None: | |
1186 | raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait)) |
|
1195 | raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait)) | |
@@ -1220,7 +1229,7 b' class List(Container):' | |||||
1220 | """An instance of a Python list.""" |
|
1229 | """An instance of a Python list.""" | |
1221 | klass = list |
|
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 | allow_none=True, **metadata): |
|
1233 | allow_none=True, **metadata): | |
1225 | """Create a List trait type from a list, set, or tuple. |
|
1234 | """Create a List trait type from a list, set, or tuple. | |
1226 |
|
1235 | |||
@@ -1249,7 +1258,7 b' class List(Container):' | |||||
1249 | minlen : Int [ default 0 ] |
|
1258 | minlen : Int [ default 0 ] | |
1250 | The minimum length of the input list |
|
1259 | The minimum length of the input list | |
1251 |
|
1260 | |||
1252 |
maxlen : Int [ default sys.max |
|
1261 | maxlen : Int [ default sys.maxsize ] | |
1253 | The maximum length of the input list |
|
1262 | The maximum length of the input list | |
1254 |
|
1263 | |||
1255 | allow_none : Bool [ default True ] |
|
1264 | allow_none : Bool [ default True ] | |
@@ -1327,10 +1336,8 b' class Tuple(Container):' | |||||
1327 | default_value = metadata.pop('default_value', None) |
|
1336 | default_value = metadata.pop('default_value', None) | |
1328 | allow_none = metadata.pop('allow_none', True) |
|
1337 | allow_none = metadata.pop('allow_none', True) | |
1329 |
|
1338 | |||
1330 | istrait = lambda t: isinstance(t, type) and issubclass(t, TraitType) |
|
|||
1331 |
|
||||
1332 | # allow Tuple((values,)): |
|
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 | default_value = traits[0] |
|
1341 | default_value = traits[0] | |
1335 | traits = () |
|
1342 | traits = () | |
1336 |
|
1343 | |||
@@ -1343,7 +1350,7 b' class Tuple(Container):' | |||||
1343 |
|
1350 | |||
1344 | self._traits = [] |
|
1351 | self._traits = [] | |
1345 | for trait in traits: |
|
1352 | for trait in traits: | |
1346 | t = trait() |
|
1353 | t = trait() if isinstance(trait, type) else trait | |
1347 | t.name = 'element' |
|
1354 | t.name = 'element' | |
1348 | self._traits.append(t) |
|
1355 | self._traits.append(t) | |
1349 |
|
1356 |
@@ -21,7 +21,7 b' from IPython.utils.py3compat import bytes_to_str' | |||||
21 | from parentpoller import ParentPollerWindows |
|
21 | from parentpoller import ParentPollerWindows | |
22 |
|
22 | |||
23 | def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0, |
|
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 | """Generates a JSON config file, including the selection of random ports. |
|
25 | """Generates a JSON config file, including the selection of random ports. | |
26 |
|
26 | |||
27 | Parameters |
|
27 | Parameters | |
@@ -54,17 +54,26 b' def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, ' | |||||
54 | fname = tempfile.mktemp('.json') |
|
54 | fname = tempfile.mktemp('.json') | |
55 |
|
55 | |||
56 | # Find open ports as necessary. |
|
56 | # Find open ports as necessary. | |
|
57 | ||||
57 | ports = [] |
|
58 | ports = [] | |
58 | ports_needed = int(shell_port <= 0) + int(iopub_port <= 0) + \ |
|
59 | ports_needed = int(shell_port <= 0) + int(iopub_port <= 0) + \ | |
59 | int(stdin_port <= 0) + int(hb_port <= 0) |
|
60 | int(stdin_port <= 0) + int(hb_port <= 0) | |
60 | for i in xrange(ports_needed): |
|
61 | if transport == 'tcp': | |
61 | sock = socket.socket() |
|
62 | for i in range(ports_needed): | |
62 | sock.bind(('', 0)) |
|
63 | sock = socket.socket() | |
63 | ports.append(sock) |
|
64 | sock.bind(('', 0)) | |
64 | for i, sock in enumerate(ports): |
|
65 | ports.append(sock) | |
65 | port = sock.getsockname()[1] |
|
66 | for i, sock in enumerate(ports): | |
66 | sock.close() |
|
67 | port = sock.getsockname()[1] | |
67 | ports[i] = port |
|
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 | if shell_port <= 0: |
|
77 | if shell_port <= 0: | |
69 | shell_port = ports.pop(0) |
|
78 | shell_port = ports.pop(0) | |
70 | if iopub_port <= 0: |
|
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 | cfg['ip'] = ip |
|
91 | cfg['ip'] = ip | |
83 | cfg['key'] = bytes_to_str(key) |
|
92 | cfg['key'] = bytes_to_str(key) | |
|
93 | cfg['transport'] = transport | |||
84 |
|
94 | |||
85 | with open(fname, 'w') as f: |
|
95 | with open(fname, 'w') as f: | |
86 | f.write(json.dumps(cfg, indent=2)) |
|
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 | creationflags=512, # CREATE_NEW_PROCESS_GROUP |
|
189 | creationflags=512, # CREATE_NEW_PROCESS_GROUP | |
180 | stdin=_stdin, stdout=_stdout, stderr=_stderr) |
|
190 | stdin=_stdin, stdout=_stdout, stderr=_stderr) | |
181 | else: |
|
191 | else: | |
182 | from _subprocess import DuplicateHandle, GetCurrentProcess, \ |
|
192 | try: | |
183 | DUPLICATE_SAME_ACCESS |
|
193 | from _winapi import DuplicateHandle, GetCurrentProcess, \ | |
|
194 | DUPLICATE_SAME_ACCESS | |||
|
195 | except: | |||
|
196 | from _subprocess import DuplicateHandle, GetCurrentProcess, \ | |||
|
197 | DUPLICATE_SAME_ACCESS | |||
184 | pid = GetCurrentProcess() |
|
198 | pid = GetCurrentProcess() | |
185 | handle = DuplicateHandle(pid, pid, pid, 0, |
|
199 | handle = DuplicateHandle(pid, pid, pid, 0, | |
186 | True, # Inheritable by new processes. |
|
200 | True, # Inheritable by new processes. |
@@ -12,6 +12,7 b'' | |||||
12 | # Imports |
|
12 | # Imports | |
13 | #----------------------------------------------------------------------------- |
|
13 | #----------------------------------------------------------------------------- | |
14 |
|
14 | |||
|
15 | import os | |||
15 | import socket |
|
16 | import socket | |
16 | import sys |
|
17 | import sys | |
17 | from threading import Thread |
|
18 | from threading import Thread | |
@@ -28,21 +29,28 b' from IPython.utils.localinterfaces import LOCALHOST' | |||||
28 | class Heartbeat(Thread): |
|
29 | class Heartbeat(Thread): | |
29 | "A simple ping-pong style heartbeat that runs in a thread." |
|
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 | Thread.__init__(self) |
|
33 | Thread.__init__(self) | |
33 | self.context = context |
|
34 | self.context = context | |
34 | self.ip, self.port = addr |
|
35 | self.transport, self.ip, self.port = addr | |
35 | if self.port == 0: |
|
36 | if self.port == 0: | |
36 | s = socket.socket() |
|
37 | if addr[0] == 'tcp': | |
37 | # '*' means all interfaces to 0MQ, which is '' to socket.socket |
|
38 | s = socket.socket() | |
38 | s.bind(('' if self.ip == '*' else self.ip, 0)) |
|
39 | # '*' means all interfaces to 0MQ, which is '' to socket.socket | |
39 | self.port = s.getsockname()[1] |
|
40 | s.bind(('' if self.ip == '*' else self.ip, 0)) | |
40 | s.close() |
|
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 | self.addr = (self.ip, self.port) |
|
48 | self.addr = (self.ip, self.port) | |
42 | self.daemon = True |
|
49 | self.daemon = True | |
43 |
|
50 | |||
44 | def run(self): |
|
51 | def run(self): | |
45 | self.socket = self.context.socket(zmq.REP) |
|
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 | zmq.device(zmq.FORWARDER, self.socket, self.socket) |
|
55 | zmq.device(zmq.FORWARDER, self.socket, self.socket) | |
48 |
|
56 |
@@ -4,7 +4,7 b' from io import StringIO' | |||||
4 |
|
4 | |||
5 | from session import extract_header, Message |
|
5 | from session import extract_header, Message | |
6 |
|
6 | |||
7 |
from IPython.utils import io, text |
|
7 | from IPython.utils import io, text | |
8 | from IPython.utils import py3compat |
|
8 | from IPython.utils import py3compat | |
9 |
|
9 | |||
10 | #----------------------------------------------------------------------------- |
|
10 | #----------------------------------------------------------------------------- | |
@@ -23,6 +23,7 b' class OutStream(object):' | |||||
23 | topic=None |
|
23 | topic=None | |
24 |
|
24 | |||
25 | def __init__(self, session, pub_socket, name): |
|
25 | def __init__(self, session, pub_socket, name): | |
|
26 | self.encoding = 'UTF-8' | |||
26 | self.session = session |
|
27 | self.session = session | |
27 | self.pub_socket = pub_socket |
|
28 | self.pub_socket = pub_socket | |
28 | self.name = name |
|
29 | self.name = name | |
@@ -73,9 +74,8 b' class OutStream(object):' | |||||
73 | else: |
|
74 | else: | |
74 | # Make sure that we're handling unicode |
|
75 | # Make sure that we're handling unicode | |
75 | if not isinstance(string, unicode): |
|
76 | if not isinstance(string, unicode): | |
76 | enc = encoding.DEFAULT_ENCODING |
|
77 | string = string.decode(self.encoding, 'replace') | |
77 | string = string.decode(enc, 'replace') |
|
78 | ||
78 |
|
||||
79 | self._buffer.write(string) |
|
79 | self._buffer.write(string) | |
80 | current_time = time.time() |
|
80 | current_time = time.time() | |
81 | if self._start <= 0: |
|
81 | if self._start <= 0: |
@@ -486,9 +486,10 b' class Kernel(Configurable):' | |||||
486 | raw=raw, output=output) |
|
486 | raw=raw, output=output) | |
487 |
|
487 | |||
488 | elif hist_access_type == 'search': |
|
488 | elif hist_access_type == 'search': | |
|
489 | n = parent['content'].get('n') | |||
489 | pattern = parent['content']['pattern'] |
|
490 | pattern = parent['content']['pattern'] | |
490 | hist = self.shell.history_manager.search(pattern, raw=raw, |
|
491 | hist = self.shell.history_manager.search(pattern, raw=raw, | |
491 |
output=output) |
|
492 | output=output, n=n) | |
492 |
|
493 | |||
493 | else: |
|
494 | else: | |
494 | hist = [] |
|
495 | hist = [] |
@@ -35,8 +35,10 b' from IPython.utils import io' | |||||
35 | from IPython.utils.localinterfaces import LOCALHOST |
|
35 | from IPython.utils.localinterfaces import LOCALHOST | |
36 | from IPython.utils.path import filefind |
|
36 | from IPython.utils.path import filefind | |
37 | from IPython.utils.py3compat import str_to_bytes |
|
37 | from IPython.utils.py3compat import str_to_bytes | |
38 |
from IPython.utils.traitlets import ( |
|
38 | from IPython.utils.traitlets import ( | |
39 | DottedObjectName) |
|
39 | Any, Instance, Dict, Unicode, Integer, Bool, CaselessStrEnum, | |
|
40 | DottedObjectName, | |||
|
41 | ) | |||
40 | from IPython.utils.importstring import import_item |
|
42 | from IPython.utils.importstring import import_item | |
41 | # local imports |
|
43 | # local imports | |
42 | from IPython.zmq.entry_point import write_connection_file |
|
44 | from IPython.zmq.entry_point import write_connection_file | |
@@ -109,6 +111,7 b' class KernelApp(BaseIPythonApplication):' | |||||
109 | self.config_file_specified = False |
|
111 | self.config_file_specified = False | |
110 |
|
112 | |||
111 | # connection info: |
|
113 | # connection info: | |
|
114 | transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True) | |||
112 | ip = Unicode(LOCALHOST, config=True, |
|
115 | ip = Unicode(LOCALHOST, config=True, | |
113 | help="Set the IP or interface on which the kernel will listen.") |
|
116 | help="Set the IP or interface on which the kernel will listen.") | |
114 | hb_port = Integer(0, config=True, help="set the heartbeat port [default: random]") |
|
117 | hb_port = Integer(0, config=True, help="set the heartbeat port [default: random]") | |
@@ -154,11 +157,12 b' class KernelApp(BaseIPythonApplication):' | |||||
154 | self.poller = ParentPollerUnix() |
|
157 | self.poller = ParentPollerUnix() | |
155 |
|
158 | |||
156 | def _bind_socket(self, s, port): |
|
159 | def _bind_socket(self, s, port): | |
157 |
iface = ' |
|
160 | iface = '%s://%s' % (self.transport, self.ip) | |
158 | if port <= 0: |
|
161 | if port <= 0 and self.transport == 'tcp': | |
159 | port = s.bind_to_random_port(iface) |
|
162 | port = s.bind_to_random_port(iface) | |
160 | else: |
|
163 | else: | |
161 | s.bind(iface + ':%i'%port) |
|
164 | c = ':' if self.transport == 'tcp' else '-' | |
|
165 | s.bind(iface + c + str(port)) | |||
162 | return port |
|
166 | return port | |
163 |
|
167 | |||
164 | def load_connection_file(self): |
|
168 | def load_connection_file(self): | |
@@ -174,6 +178,7 b' class KernelApp(BaseIPythonApplication):' | |||||
174 | with open(fname) as f: |
|
178 | with open(fname) as f: | |
175 | s = f.read() |
|
179 | s = f.read() | |
176 | cfg = json.loads(s) |
|
180 | cfg = json.loads(s) | |
|
181 | self.transport = cfg.get('transport', self.transport) | |||
177 | if self.ip == LOCALHOST and 'ip' in cfg: |
|
182 | if self.ip == LOCALHOST and 'ip' in cfg: | |
178 | # not overridden by config or cl_args |
|
183 | # not overridden by config or cl_args | |
179 | self.ip = cfg['ip'] |
|
184 | self.ip = cfg['ip'] | |
@@ -191,7 +196,7 b' class KernelApp(BaseIPythonApplication):' | |||||
191 | cf = os.path.join(self.profile_dir.security_dir, self.connection_file) |
|
196 | cf = os.path.join(self.profile_dir.security_dir, self.connection_file) | |
192 | else: |
|
197 | else: | |
193 | cf = self.connection_file |
|
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 | shell_port=self.shell_port, stdin_port=self.stdin_port, hb_port=self.hb_port, |
|
200 | shell_port=self.shell_port, stdin_port=self.stdin_port, hb_port=self.hb_port, | |
196 | iopub_port=self.iopub_port) |
|
201 | iopub_port=self.iopub_port) | |
197 |
|
202 | |||
@@ -204,6 +209,19 b' class KernelApp(BaseIPythonApplication):' | |||||
204 | os.remove(cf) |
|
209 | os.remove(cf) | |
205 | except (IOError, OSError): |
|
210 | except (IOError, OSError): | |
206 | pass |
|
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 | def init_connection_file(self): |
|
226 | def init_connection_file(self): | |
209 | if not self.connection_file: |
|
227 | if not self.connection_file: | |
@@ -238,7 +256,7 b' class KernelApp(BaseIPythonApplication):' | |||||
238 | # heartbeat doesn't share context, because it mustn't be blocked |
|
256 | # heartbeat doesn't share context, because it mustn't be blocked | |
239 | # by the GIL, which is accessed by libzmq when freeing zero-copy messages |
|
257 | # by the GIL, which is accessed by libzmq when freeing zero-copy messages | |
240 | hb_ctx = zmq.Context() |
|
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 | self.hb_port = self.heartbeat.port |
|
260 | self.hb_port = self.heartbeat.port | |
243 | self.log.debug("Heartbeat REP Channel on port: %i"%self.hb_port) |
|
261 | self.log.debug("Heartbeat REP Channel on port: %i"%self.hb_port) | |
244 | self.heartbeat.start() |
|
262 | self.heartbeat.start() |
@@ -37,7 +37,7 b' from zmq.eventloop import ioloop, zmqstream' | |||||
37 | from IPython.config.loader import Config |
|
37 | from IPython.config.loader import Config | |
38 | from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS |
|
38 | from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS | |
39 | from IPython.utils.traitlets import ( |
|
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 | from IPython.utils.py3compat import str_to_bytes |
|
42 | from IPython.utils.py3compat import str_to_bytes | |
43 | from IPython.zmq.entry_point import write_connection_file |
|
43 | from IPython.zmq.entry_point import write_connection_file | |
@@ -103,7 +103,7 b' class ZMQSocketChannel(Thread):' | |||||
103 | The ZMQ context to use. |
|
103 | The ZMQ context to use. | |
104 | session : :class:`session.Session` |
|
104 | session : :class:`session.Session` | |
105 | The session to use. |
|
105 | The session to use. | |
106 |
address : |
|
106 | address : zmq url | |
107 | Standard (ip, port) tuple that the kernel is listening on. |
|
107 | Standard (ip, port) tuple that the kernel is listening on. | |
108 | """ |
|
108 | """ | |
109 | super(ZMQSocketChannel, self).__init__() |
|
109 | super(ZMQSocketChannel, self).__init__() | |
@@ -111,9 +111,11 b' class ZMQSocketChannel(Thread):' | |||||
111 |
|
111 | |||
112 | self.context = context |
|
112 | self.context = context | |
113 | self.session = session |
|
113 | self.session = session | |
114 |
if address |
|
114 | if isinstance(address, tuple): | |
115 | message = 'The port number for a channel cannot be 0.' |
|
115 | if address[1] == 0: | |
116 | raise InvalidPortNumber(message) |
|
116 | message = 'The port number for a channel cannot be 0.' | |
|
117 | raise InvalidPortNumber(message) | |||
|
118 | address = "tcp://%s:%i" % address | |||
117 | self._address = address |
|
119 | self._address = address | |
118 | atexit.register(self._notice_exit) |
|
120 | atexit.register(self._notice_exit) | |
119 |
|
121 | |||
@@ -149,10 +151,7 b' class ZMQSocketChannel(Thread):' | |||||
149 |
|
151 | |||
150 | @property |
|
152 | @property | |
151 | def address(self): |
|
153 | def address(self): | |
152 |
"""Get the channel's address as a |
|
154 | """Get the channel's address as a zmq url string ('tcp://127.0.0.1:5555'). | |
153 |
|
||||
154 | By the default, the address is (localhost, 0), where 0 means a random |
|
|||
155 | port. |
|
|||
156 | """ |
|
155 | """ | |
157 | return self._address |
|
156 | return self._address | |
158 |
|
157 | |||
@@ -196,7 +195,7 b' class ShellSocketChannel(ZMQSocketChannel):' | |||||
196 | """The thread's main activity. Call start() instead.""" |
|
195 | """The thread's main activity. Call start() instead.""" | |
197 | self.socket = self.context.socket(zmq.DEALER) |
|
196 | self.socket = self.context.socket(zmq.DEALER) | |
198 | self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) |
|
197 | self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) | |
199 |
self.socket.connect( |
|
198 | self.socket.connect(self.address) | |
200 | self.stream = zmqstream.ZMQStream(self.socket, self.ioloop) |
|
199 | self.stream = zmqstream.ZMQStream(self.socket, self.ioloop) | |
201 | self.stream.on_recv(self._handle_recv) |
|
200 | self.stream.on_recv(self._handle_recv) | |
202 | self._run_loop() |
|
201 | self._run_loop() | |
@@ -242,15 +241,16 b' class ShellSocketChannel(ZMQSocketChannel):' | |||||
242 | :func:`repr` as values. |
|
241 | :func:`repr` as values. | |
243 |
|
242 | |||
244 | user_expressions : dict, optional |
|
243 | user_expressions : dict, optional | |
245 |
A dict |
|
244 | A dict mapping names to expressions to be evaluated in the user's | |
246 | namespace. They will come back as a dict with these names as keys |
|
245 | dict. The expression values are returned as strings formatted using | |
247 |
|
|
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 |
|
251 | Some frontends (e.g. the Notebook) do not support stdin requests. | |
250 | Flag for |
|
252 | If raw_input is called from code executed from such a frontend, a | |
251 | A dict with string keys and to pull from the user's |
|
253 | StdinNotImplementedError will be raised. | |
252 | namespace. They will come back as a dict with these names as keys |
|
|||
253 | and their :func:`repr` as values. |
|
|||
254 |
|
254 | |||
255 | Returns |
|
255 | Returns | |
256 | ------- |
|
256 | ------- | |
@@ -395,7 +395,7 b' class SubSocketChannel(ZMQSocketChannel):' | |||||
395 | self.socket = self.context.socket(zmq.SUB) |
|
395 | self.socket = self.context.socket(zmq.SUB) | |
396 | self.socket.setsockopt(zmq.SUBSCRIBE,b'') |
|
396 | self.socket.setsockopt(zmq.SUBSCRIBE,b'') | |
397 | self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) |
|
397 | self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) | |
398 |
self.socket.connect( |
|
398 | self.socket.connect(self.address) | |
399 | self.stream = zmqstream.ZMQStream(self.socket, self.ioloop) |
|
399 | self.stream = zmqstream.ZMQStream(self.socket, self.ioloop) | |
400 | self.stream.on_recv(self._handle_recv) |
|
400 | self.stream.on_recv(self._handle_recv) | |
401 | self._run_loop() |
|
401 | self._run_loop() | |
@@ -461,7 +461,7 b' class StdInSocketChannel(ZMQSocketChannel):' | |||||
461 | """The thread's main activity. Call start() instead.""" |
|
461 | """The thread's main activity. Call start() instead.""" | |
462 | self.socket = self.context.socket(zmq.DEALER) |
|
462 | self.socket = self.context.socket(zmq.DEALER) | |
463 | self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) |
|
463 | self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) | |
464 |
self.socket.connect( |
|
464 | self.socket.connect(self.address) | |
465 | self.stream = zmqstream.ZMQStream(self.socket, self.ioloop) |
|
465 | self.stream = zmqstream.ZMQStream(self.socket, self.ioloop) | |
466 | self.stream.on_recv(self._handle_recv) |
|
466 | self.stream.on_recv(self._handle_recv) | |
467 | self._run_loop() |
|
467 | self._run_loop() | |
@@ -520,7 +520,7 b' class HBSocketChannel(ZMQSocketChannel):' | |||||
520 | self.socket.close() |
|
520 | self.socket.close() | |
521 | self.socket = self.context.socket(zmq.REQ) |
|
521 | self.socket = self.context.socket(zmq.REQ) | |
522 | self.socket.setsockopt(zmq.LINGER, 0) |
|
522 | self.socket.setsockopt(zmq.LINGER, 0) | |
523 |
self.socket.connect( |
|
523 | self.socket.connect(self.address) | |
524 |
|
524 | |||
525 | self.poller.register(self.socket, zmq.POLLIN) |
|
525 | self.poller.register(self.socket, zmq.POLLIN) | |
526 |
|
526 | |||
@@ -659,6 +659,10 b' class KernelManager(HasTraits):' | |||||
659 |
|
659 | |||
660 | # The addresses for the communication channels. |
|
660 | # The addresses for the communication channels. | |
661 | connection_file = Unicode('') |
|
661 | connection_file = Unicode('') | |
|
662 | ||||
|
663 | transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp') | |||
|
664 | ||||
|
665 | ||||
662 | ip = Unicode(LOCALHOST) |
|
666 | ip = Unicode(LOCALHOST) | |
663 | def _ip_changed(self, name, old, new): |
|
667 | def _ip_changed(self, name, old, new): | |
664 | if new == '*': |
|
668 | if new == '*': | |
@@ -747,7 +751,20 b' class KernelManager(HasTraits):' | |||||
747 | self._connection_file_written = False |
|
751 | self._connection_file_written = False | |
748 | try: |
|
752 | try: | |
749 | os.remove(self.connection_file) |
|
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 | pass |
|
768 | pass | |
752 |
|
769 | |||
753 | def load_connection_file(self): |
|
770 | def load_connection_file(self): | |
@@ -755,6 +772,9 b' class KernelManager(HasTraits):' | |||||
755 | with open(self.connection_file) as f: |
|
772 | with open(self.connection_file) as f: | |
756 | cfg = json.loads(f.read()) |
|
773 | cfg = json.loads(f.read()) | |
757 |
|
774 | |||
|
775 | from pprint import pprint | |||
|
776 | pprint(cfg) | |||
|
777 | self.transport = cfg.get('transport', 'tcp') | |||
758 | self.ip = cfg['ip'] |
|
778 | self.ip = cfg['ip'] | |
759 | self.shell_port = cfg['shell_port'] |
|
779 | self.shell_port = cfg['shell_port'] | |
760 | self.stdin_port = cfg['stdin_port'] |
|
780 | self.stdin_port = cfg['stdin_port'] | |
@@ -767,7 +787,7 b' class KernelManager(HasTraits):' | |||||
767 | if self._connection_file_written: |
|
787 | if self._connection_file_written: | |
768 | return |
|
788 | return | |
769 | self.connection_file,cfg = write_connection_file(self.connection_file, |
|
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 | stdin_port=self.stdin_port, iopub_port=self.iopub_port, |
|
791 | stdin_port=self.stdin_port, iopub_port=self.iopub_port, | |
772 | shell_port=self.shell_port, hb_port=self.hb_port) |
|
792 | shell_port=self.shell_port, hb_port=self.hb_port) | |
773 | # write_connection_file also sets default ports: |
|
793 | # write_connection_file also sets default ports: | |
@@ -794,7 +814,7 b' class KernelManager(HasTraits):' | |||||
794 | **kw : optional |
|
814 | **kw : optional | |
795 | See respective options for IPython and Python kernels. |
|
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 | raise RuntimeError("Can only launch a kernel on a local interface. " |
|
818 | raise RuntimeError("Can only launch a kernel on a local interface. " | |
799 | "Make sure that the '*_address' attributes are " |
|
819 | "Make sure that the '*_address' attributes are " | |
800 | "configured properly. " |
|
820 | "configured properly. " | |
@@ -811,8 +831,9 b' class KernelManager(HasTraits):' | |||||
811 | self.kernel = launch_kernel(fname=self.connection_file, **kw) |
|
831 | self.kernel = launch_kernel(fname=self.connection_file, **kw) | |
812 |
|
832 | |||
813 | def shutdown_kernel(self, restart=False): |
|
833 | def shutdown_kernel(self, restart=False): | |
814 |
""" Attempts to the stop the kernel process cleanly. |
|
834 | """ Attempts to the stop the kernel process cleanly. | |
815 | cannot be stopped, it is killed, if possible. |
|
835 | ||
|
836 | If the kernel cannot be stopped and the kernel is local, it is killed. | |||
816 | """ |
|
837 | """ | |
817 | # FIXME: Shutdown does not work on Windows due to ZMQ errors! |
|
838 | # FIXME: Shutdown does not work on Windows due to ZMQ errors! | |
818 | if sys.platform == 'win32': |
|
839 | if sys.platform == 'win32': | |
@@ -893,13 +914,17 b' class KernelManager(HasTraits):' | |||||
893 | return self.kernel is not None |
|
914 | return self.kernel is not None | |
894 |
|
915 | |||
895 | def kill_kernel(self): |
|
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 | if self.has_kernel: |
|
921 | if self.has_kernel: | |
898 | # Pause the heart beat channel if it exists. |
|
922 | # Pause the heart beat channel if it exists. | |
899 | if self._hb_channel is not None: |
|
923 | if self._hb_channel is not None: | |
900 | self._hb_channel.pause() |
|
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 | try: |
|
928 | try: | |
904 | self.kernel.kill() |
|
929 | self.kernel.kill() | |
905 | except OSError as e: |
|
930 | except OSError as e: | |
@@ -914,13 +939,18 b' class KernelManager(HasTraits):' | |||||
914 | from errno import ESRCH |
|
939 | from errno import ESRCH | |
915 | if e.errno != ESRCH: |
|
940 | if e.errno != ESRCH: | |
916 | raise |
|
941 | raise | |
|
942 | ||||
|
943 | # Block until the kernel terminates. | |||
|
944 | self.kernel.wait() | |||
917 | self.kernel = None |
|
945 | self.kernel = None | |
918 | else: |
|
946 | else: | |
919 | raise RuntimeError("Cannot kill kernel. No kernel is running!") |
|
947 | raise RuntimeError("Cannot kill kernel. No kernel is running!") | |
920 |
|
948 | |||
921 | def interrupt_kernel(self): |
|
949 | def interrupt_kernel(self): | |
922 |
""" Interrupts the kernel. |
|
950 | """ Interrupts the kernel. | |
923 | well supported on all platforms. |
|
951 | ||
|
952 | Unlike ``signal_kernel``, this operation is well supported on all | |||
|
953 | platforms. | |||
924 | """ |
|
954 | """ | |
925 | if self.has_kernel: |
|
955 | if self.has_kernel: | |
926 | if sys.platform == 'win32': |
|
956 | if sys.platform == 'win32': | |
@@ -932,8 +962,10 b' class KernelManager(HasTraits):' | |||||
932 | raise RuntimeError("Cannot interrupt kernel. No kernel is running!") |
|
962 | raise RuntimeError("Cannot interrupt kernel. No kernel is running!") | |
933 |
|
963 | |||
934 | def signal_kernel(self, signum): |
|
964 | def signal_kernel(self, signum): | |
935 |
""" Sends a signal to the kernel. |
|
965 | """ Sends a signal to the kernel. | |
936 | supported on Windows, this function is only useful on Unix systems. |
|
966 | ||
|
967 | Note that since only SIGTERM is supported on Windows, this function is | |||
|
968 | only useful on Unix systems. | |||
937 | """ |
|
969 | """ | |
938 | if self.has_kernel: |
|
970 | if self.has_kernel: | |
939 | self.kernel.send_signal(signum) |
|
971 | self.kernel.send_signal(signum) | |
@@ -961,13 +993,21 b' class KernelManager(HasTraits):' | |||||
961 | # Channels used for communication with the kernel: |
|
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 | @property |
|
1003 | @property | |
965 | def shell_channel(self): |
|
1004 | def shell_channel(self): | |
966 | """Get the REQ socket channel object to make requests of the kernel.""" |
|
1005 | """Get the REQ socket channel object to make requests of the kernel.""" | |
967 | if self._shell_channel is None: |
|
1006 | if self._shell_channel is None: | |
968 | self._shell_channel = self.shell_channel_class(self.context, |
|
1007 | self._shell_channel = self.shell_channel_class(self.context, | |
969 | self.session, |
|
1008 | self.session, | |
970 |
|
|
1009 | self._make_url(self.shell_port), | |
|
1010 | ) | |||
971 | return self._shell_channel |
|
1011 | return self._shell_channel | |
972 |
|
1012 | |||
973 | @property |
|
1013 | @property | |
@@ -976,7 +1016,8 b' class KernelManager(HasTraits):' | |||||
976 | if self._sub_channel is None: |
|
1016 | if self._sub_channel is None: | |
977 | self._sub_channel = self.sub_channel_class(self.context, |
|
1017 | self._sub_channel = self.sub_channel_class(self.context, | |
978 | self.session, |
|
1018 | self.session, | |
979 |
|
|
1019 | self._make_url(self.iopub_port), | |
|
1020 | ) | |||
980 | return self._sub_channel |
|
1021 | return self._sub_channel | |
981 |
|
1022 | |||
982 | @property |
|
1023 | @property | |
@@ -984,8 +1025,9 b' class KernelManager(HasTraits):' | |||||
984 | """Get the REP socket channel object to handle stdin (raw_input).""" |
|
1025 | """Get the REP socket channel object to handle stdin (raw_input).""" | |
985 | if self._stdin_channel is None: |
|
1026 | if self._stdin_channel is None: | |
986 | self._stdin_channel = self.stdin_channel_class(self.context, |
|
1027 | self._stdin_channel = self.stdin_channel_class(self.context, | |
987 | self.session, |
|
1028 | self.session, | |
988 |
|
|
1029 | self._make_url(self.stdin_port), | |
|
1030 | ) | |||
989 | return self._stdin_channel |
|
1031 | return self._stdin_channel | |
990 |
|
1032 | |||
991 | @property |
|
1033 | @property | |
@@ -994,6 +1036,7 b' class KernelManager(HasTraits):' | |||||
994 | kernel is alive.""" |
|
1036 | kernel is alive.""" | |
995 | if self._hb_channel is None: |
|
1037 | if self._hb_channel is None: | |
996 | self._hb_channel = self.hb_channel_class(self.context, |
|
1038 | self._hb_channel = self.hb_channel_class(self.context, | |
997 |
|
|
1039 | self.session, | |
998 |
|
|
1040 | self._make_url(self.hb_port), | |
|
1041 | ) | |||
999 | return self._hb_channel |
|
1042 | return self._hb_channel |
@@ -100,7 +100,10 b' class ParentPollerWindows(Thread):' | |||||
100 | def run(self): |
|
100 | def run(self): | |
101 | """ Run the poll loop. This method never returns. |
|
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 | # Build the list of handle to listen on. |
|
108 | # Build the list of handle to listen on. | |
106 | handles = [] |
|
109 | handles = [] |
@@ -10,7 +10,7 b' import sys' | |||||
10 |
|
10 | |||
11 | # Third-party imports |
|
11 | # Third-party imports | |
12 | import matplotlib |
|
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 | from matplotlib._pylab_helpers import Gcf |
|
14 | from matplotlib._pylab_helpers import Gcf | |
15 |
|
15 | |||
16 | # Local imports. |
|
16 | # Local imports. | |
@@ -207,3 +207,9 b' def flush_figures():' | |||||
207 | show._to_draw = [] |
|
207 | show._to_draw = [] | |
208 | show._draw_called = False |
|
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 | from zmq.eventloop.ioloop import IOLoop |
|
43 | from zmq.eventloop.ioloop import IOLoop | |
44 | from zmq.eventloop.zmqstream import ZMQStream |
|
44 | from zmq.eventloop.zmqstream import ZMQStream | |
45 |
|
45 | |||
|
46 | import IPython | |||
46 | from IPython.config.application import Application, boolean_flag |
|
47 | from IPython.config.application import Application, boolean_flag | |
47 | from IPython.config.configurable import Configurable, LoggingConfigurable |
|
48 | from IPython.config.configurable import Configurable, LoggingConfigurable | |
48 | from IPython.utils.importstring import import_item |
|
49 | from IPython.utils.importstring import import_item | |
@@ -74,7 +75,7 b' def squash_unicode(obj):' | |||||
74 | # globals and defaults |
|
75 | # globals and defaults | |
75 | #----------------------------------------------------------------------------- |
|
76 | #----------------------------------------------------------------------------- | |
76 |
|
77 | |||
77 |
|
78 | _version_info_list = list(IPython.version_info) | ||
78 | # ISO8601-ify datetime objects |
|
79 | # ISO8601-ify datetime objects | |
79 | json_packer = lambda obj: jsonapi.dumps(obj, default=date_default) |
|
80 | json_packer = lambda obj: jsonapi.dumps(obj, default=date_default) | |
80 | json_unpacker = lambda s: extract_dates(jsonapi.loads(s)) |
|
81 | json_unpacker = lambda s: extract_dates(jsonapi.loads(s)) | |
@@ -187,6 +188,7 b' class Message(object):' | |||||
187 |
|
188 | |||
188 | def msg_header(msg_id, msg_type, username, session): |
|
189 | def msg_header(msg_id, msg_type, username, session): | |
189 | date = datetime.now() |
|
190 | date = datetime.now() | |
|
191 | version = _version_info_list | |||
190 | return locals() |
|
192 | return locals() | |
191 |
|
193 | |||
192 | def extract_header(msg_or_header): |
|
194 | def extract_header(msg_or_header): |
@@ -38,7 +38,7 b' from IPython.lib.kernel import (' | |||||
38 | get_connection_file, get_connection_info, connect_qtconsole |
|
38 | get_connection_file, get_connection_info, connect_qtconsole | |
39 | ) |
|
39 | ) | |
40 | from IPython.testing.skipdoctest import skip_doctest |
|
40 | from IPython.testing.skipdoctest import skip_doctest | |
41 | from IPython.utils import io |
|
41 | from IPython.utils import io, openpy | |
42 | from IPython.utils.jsonutil import json_clean, encode_images |
|
42 | from IPython.utils.jsonutil import json_clean, encode_images | |
43 | from IPython.utils.process import arg_split |
|
43 | from IPython.utils.process import arg_split | |
44 | from IPython.utils import py3compat |
|
44 | from IPython.utils import py3compat | |
@@ -355,7 +355,9 b' class KernelMagics(Magics):' | |||||
355 |
|
355 | |||
356 | cont = open(arg_s).read() |
|
356 | cont = open(arg_s).read() | |
357 | if arg_s.endswith('.py'): |
|
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 | page.page(cont) |
|
361 | page.page(cont) | |
360 |
|
362 | |||
361 | more = line_magic('more')(less) |
|
363 | more = line_magic('more')(less) |
@@ -1,4 +1,5 b'' | |||||
1 | include README.rst |
|
1 | include README.rst | |
|
2 | include COPYING.txt | |||
2 | include ipython.py |
|
3 | include ipython.py | |
3 | include setupbase.py |
|
4 | include setupbase.py | |
4 | include setupegg.py |
|
5 | include setupegg.py |
@@ -1,9 +1,12 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
1 | """A simple interactive demo to illustrate the use of IPython's Demo class. |
|
2 | """A simple interactive demo to illustrate the use of IPython's Demo class. | |
2 |
|
3 | |||
3 | Any python script can be run as a demo, but that does little more than showing |
|
4 | Any python script can be run as a demo, but that does little more than showing | |
4 | it on-screen, syntax-highlighted in one shot. If you add a little simple |
|
5 | it on-screen, syntax-highlighted in one shot. If you add a little simple | |
5 | markup, you can stop at specified intervals and return to the ipython prompt, |
|
6 | markup, you can stop at specified intervals and return to the ipython prompt, | |
6 | resuming execution later. |
|
7 | resuming execution later. | |
|
8 | ||||
|
9 | This is a unicode test, åäö | |||
7 | """ |
|
10 | """ | |
8 | from __future__ import print_function |
|
11 | from __future__ import print_function | |
9 |
|
12 |
@@ -204,25 +204,16 b'' | |||||
204 | "collapsed": true, |
|
204 | "collapsed": true, | |
205 | "input": [ |
|
205 | "input": [ | |
206 | "import sys, time\n", |
|
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 | "\n", |
|
207 | "\n", | |
213 | "class ProgressBar:\n", |
|
208 | "class ProgressBar:\n", | |
214 | " def __init__(self, iterations):\n", |
|
209 | " def __init__(self, iterations):\n", | |
215 | " self.iterations = iterations\n", |
|
210 | " self.iterations = iterations\n", | |
216 | " self.prog_bar = '[]'\n", |
|
211 | " self.prog_bar = '[]'\n", | |
217 | " self.fill_char = '*'\n", |
|
212 | " self.fill_char = '*'\n", | |
218 |
" self.width = |
|
213 | " self.width = 50\n", | |
219 | " self.__update_amount(0)\n", |
|
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 | "\n", |
|
215 | "\n", | |
225 |
" def animate |
|
216 | " def animate(self, iter):\n", | |
226 | " print '\\r', self,\n", |
|
217 | " print '\\r', self,\n", | |
227 | " sys.stdout.flush()\n", |
|
218 | " sys.stdout.flush()\n", | |
228 | " self.update_iteration(iter + 1)\n", |
|
219 | " self.update_iteration(iter + 1)\n", | |
@@ -254,19 +245,12 b'' | |||||
254 | "input": [ |
|
245 | "input": [ | |
255 | "p = ProgressBar(1000)\n", |
|
246 | "p = ProgressBar(1000)\n", | |
256 | "for i in range(1001):\n", |
|
247 | "for i in range(1001):\n", | |
|
248 | " time.sleep(0.002)\n", | |||
257 | " p.animate(i)" |
|
249 | " p.animate(i)" | |
258 | ], |
|
250 | ], | |
259 | "language": "python", |
|
251 | "language": "python", | |
260 | "metadata": {}, |
|
252 | "metadata": {}, | |
261 | "outputs": [] |
|
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 | "metadata": {} |
|
256 | "metadata": {} |
@@ -24,26 +24,7 b' if ON_RTD:' | |||||
24 | # see |
|
24 | # see | |
25 | # http://read-the-docs.readthedocs.org/en/latest/faq.html |
|
25 | # http://read-the-docs.readthedocs.org/en/latest/faq.html | |
26 | tags.add('rtd') |
|
26 | tags.add('rtd') | |
27 | class Mock(object): |
|
27 | ||
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 |
|
||||
47 | # If your extensions are in another directory, add it here. If the directory |
|
28 | # If your extensions are in another directory, add it here. If the directory | |
48 | # is relative to the documentation root, use os.path.abspath to make it |
|
29 | # is relative to the documentation root, use os.path.abspath to make it | |
49 | # absolute, like shown here. |
|
30 | # absolute, like shown here. | |
@@ -63,9 +44,9 b" execfile('../../IPython/core/release.py',iprelease)" | |||||
63 | # Add any Sphinx extension module names here, as strings. They can be extensions |
|
44 | # Add any Sphinx extension module names here, as strings. They can be extensions | |
64 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. |
|
45 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. | |
65 | extensions = [ |
|
46 | extensions = [ | |
66 |
|
|
47 | 'matplotlib.sphinxext.mathmpl', | |
67 | 'matplotlib.sphinxext.only_directives', |
|
48 | 'matplotlib.sphinxext.only_directives', | |
68 |
|
|
49 | 'matplotlib.sphinxext.plot_directive', | |
69 | 'sphinx.ext.autodoc', |
|
50 | 'sphinx.ext.autodoc', | |
70 | 'sphinx.ext.doctest', |
|
51 | 'sphinx.ext.doctest', | |
71 | 'inheritance_diagram', |
|
52 | 'inheritance_diagram', |
@@ -43,7 +43,7 b' functions to load and unload it. Here is a template::' | |||||
43 | def load_ipython_extension(ipython): |
|
43 | def load_ipython_extension(ipython): | |
44 | # The `ipython` argument is the currently active `InteractiveShell` |
|
44 | # The `ipython` argument is the currently active `InteractiveShell` | |
45 | # instance, which can be used in any way. This allows you to register |
|
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 | def unload_ipython_extension(ipython): |
|
48 | def unload_ipython_extension(ipython): | |
49 | # If you want your extension to be unloadable, put that logic here. |
|
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 | When your extension is ready for general use, please add it to the `extensions |
|
70 | When your extension is ready for general use, please add it to the `extensions | |
71 | index <http://wiki.ipython.org/Extensions_Index>`_. |
|
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 | .. _bundled_extensions: |
|
73 | .. _bundled_extensions: | |
97 |
|
74 | |||
98 | Extensions bundled with IPython |
|
75 | Extensions bundled with IPython |
@@ -627,7 +627,7 b' Message type: ``history_request``::' | |||||
627 | 'start' : int, |
|
627 | 'start' : int, | |
628 | 'stop' : int, |
|
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 | 'n' : int, |
|
631 | 'n' : int, | |
632 |
|
632 | |||
633 | # If hist_access_type is 'search', get cells matching the specified glob |
|
633 | # If hist_access_type is 'search', get cells matching the specified glob |
@@ -337,7 +337,7 b' this is just a summary:' | |||||
337 | * **%pdoc <object>**: Print (or run through a pager if too long) the |
|
337 | * **%pdoc <object>**: Print (or run through a pager if too long) the | |
338 | docstring for an object. If the given object is a class, it will |
|
338 | docstring for an object. If the given object is a class, it will | |
339 | print both the class and the constructor docstrings. |
|
339 | print both the class and the constructor docstrings. | |
340 |
* **%pdef <object>**: Print the |
|
340 | * **%pdef <object>**: Print the call signature for any callable | |
341 | object. If the object is a class, print the constructor information. |
|
341 | object. If the object is a class, print the constructor information. | |
342 | * **%psource <object>**: Print (or run through a pager if too long) |
|
342 | * **%psource <object>**: Print (or run through a pager if too long) | |
343 | the source code for an object. |
|
343 | the source code for an object. |
@@ -386,19 +386,20 b' weighted: Weighted Two-Bin Random' | |||||
386 | Greedy Assignment |
|
386 | Greedy Assignment | |
387 | ----------------- |
|
387 | ----------------- | |
388 |
|
388 | |||
389 |
Tasks |
|
389 | Tasks can be assigned greedily as they are submitted. If their dependencies are | |
390 | met, they will be assigned to an engine right away, and multiple tasks can be |
|
390 | met, they will be assigned to an engine right away, and multiple tasks can be | |
391 | assigned to an engine at a given time. This limit is set with the |
|
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 | .. sourcecode:: python |
|
395 | .. sourcecode:: python | |
395 |
|
396 | |||
396 | # the most common choices are: |
|
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 | # or |
|
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 | tasks that can be outstanding on a given engine. This greatly benefits the |
|
403 | tasks that can be outstanding on a given engine. This greatly benefits the | |
403 | latency of execution, because network traffic can be hidden behind computation. |
|
404 | latency of execution, because network traffic can be hidden behind computation. | |
404 | However, this means that workload is assigned without knowledge of how long |
|
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 | submitting a collection of heterogeneous tasks all at once. You can limit this |
|
407 | submitting a collection of heterogeneous tasks all at once. You can limit this | |
407 | effect by setting hwm to a positive integer, 1 being maximum load-balancing (a |
|
408 | effect by setting hwm to a positive integer, 1 being maximum load-balancing (a | |
408 | task will never be waiting if there is an idle engine), and any larger number |
|
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 | In practice, some users have been confused by having this optimization on by |
|
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 | but has more obvious behavior and won't result in assigning too many tasks to |
|
414 | but has more obvious behavior and won't result in assigning too many tasks to | |
414 | some engines in heterogeneous cases. |
|
415 | some engines in heterogeneous cases. | |
415 |
|
416 |
@@ -230,6 +230,7 b" if 'setuptools' in sys.modules:" | |||||
230 | setuptools_extra_args['entry_points'] = find_scripts(True) |
|
230 | setuptools_extra_args['entry_points'] = find_scripts(True) | |
231 | setup_args['extras_require'] = dict( |
|
231 | setup_args['extras_require'] = dict( | |
232 | parallel = 'pyzmq>=2.1.4', |
|
232 | parallel = 'pyzmq>=2.1.4', | |
|
233 | qtconsole = 'pygments', | |||
233 | zmq = 'pyzmq>=2.1.4', |
|
234 | zmq = 'pyzmq>=2.1.4', | |
234 | doc = 'Sphinx>=0.3', |
|
235 | doc = 'Sphinx>=0.3', | |
235 | test = 'nose>=0.10.1', |
|
236 | test = 'nose>=0.10.1', |
@@ -204,12 +204,12 b' def find_data_files():' | |||||
204 | manpagebase = pjoin('share', 'man', 'man1') |
|
204 | manpagebase = pjoin('share', 'man', 'man1') | |
205 |
|
205 | |||
206 | # Simple file lists can be made by hand |
|
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 | if not manpages: |
|
208 | if not manpages: | |
209 | # When running from a source tree, the manpages aren't gzipped |
|
209 | # When running from a source tree, the manpages aren't gzipped | |
210 |
manpages = |
|
210 | manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)] | |
211 | igridhelpfiles = filter(isfile, |
|
211 | ||
212 |
|
|
212 | igridhelpfiles = [f for f in glob(pjoin('IPython','extensions','igrid_help.*')) if isfile(f)] | |
213 |
|
213 | |||
214 | # For nested structures, use the utility above |
|
214 | # For nested structures, use the utility above | |
215 | example_files = make_dir_struct( |
|
215 | example_files = make_dir_struct( |
@@ -6,6 +6,7 b' This should ONLY be run at real release time.' | |||||
6 | from __future__ import print_function |
|
6 | from __future__ import print_function | |
7 |
|
7 | |||
8 | from toollib import * |
|
8 | from toollib import * | |
|
9 | from gh_api import post_download | |||
9 |
|
10 | |||
10 | # Get main ipython dir, this will raise if it doesn't pass some checks |
|
11 | # Get main ipython dir, this will raise if it doesn't pass some checks | |
11 | ipdir = get_ipdir() |
|
12 | ipdir = get_ipdir() | |
@@ -53,6 +54,11 b" sh(sdists + ' upload')" | |||||
53 | cd(distdir) |
|
54 | cd(distdir) | |
54 | print( 'Uploading distribution files...') |
|
55 | print( 'Uploading distribution files...') | |
55 |
|
56 | |||
|
57 | for fname in os.listdir('.'): | |||
|
58 | print('uploading %s to GitHub' % fname) | |||
|
59 | desc = "IPython %s source distribution" % version | |||
|
60 | post_download("ipython/ipython", fname, description=desc) | |||
|
61 | ||||
56 | # Make target dir if it doesn't exist |
|
62 | # Make target dir if it doesn't exist | |
57 | sh('ssh %s "mkdir -p %s/release/%s" ' % (archive_user, archive_dir, version)) |
|
63 | sh('ssh %s "mkdir -p %s/release/%s" ' % (archive_user, archive_dir, version)) | |
58 | sh('scp * %s' % release_site) |
|
64 | sh('scp * %s' % release_site) |
@@ -23,20 +23,34 b' except ImportError:' | |||||
23 |
|
23 | |||
24 | github = '--github' in sys.argv |
|
24 | github = '--github' in sys.argv | |
25 |
|
25 | |||
26 |
cmd_t = "{py} setup.py bdist_wininst |
|
26 | cmd_t = "{py} setup.py bdist_wininst" | |
27 |
|
27 | |||
28 | pypi = '--pypi' in sys.argv |
|
28 | pypi = '--pypi' in sys.argv | |
29 | pypi_cmd_t = "python setup.py upload_wininst -f {fname}" |
|
29 | pypi_cmd_t = "python setup.py upload_wininst -f {fname}" | |
30 |
|
30 | |||
31 | for py in ['python', 'python3']: |
|
31 | # Windows Python cannot normally cross-compile, | |
|
32 | # so you must have 4 Pythons to make 4 installers: | |||
|
33 | # http://docs.python.org/2/distutils/builtdist.html#cross-compiling-on-windows | |||
|
34 | ||||
|
35 | pythons = { | |||
|
36 | 2: { | |||
|
37 | 'win32' : r'C:\\Python27\Python.exe', | |||
|
38 | 'win-amd64': r'C:\\Python27_64\Python.exe', | |||
|
39 | }, | |||
|
40 | 3: { | |||
|
41 | 'win32' : r'C:\\Python33\Python.exe', | |||
|
42 | 'win-amd64': r'C:\\Python33_64\Python.exe', | |||
|
43 | }, | |||
|
44 | } | |||
|
45 | ||||
|
46 | for v,plat_py in pythons.items(): | |||
32 | # deliberately mangle the name, |
|
47 | # deliberately mangle the name, | |
33 | # so easy_install doesn't find these and do horrible wrong things |
|
48 | # so easy_install doesn't find these and do horrible wrong things | |
34 | v = 3 if py.endswith('3') else 2 |
|
|||
35 | try: |
|
49 | try: | |
36 | shutil.rmtree('build') |
|
50 | shutil.rmtree('build') | |
37 | except OSError: |
|
51 | except OSError: | |
38 | pass |
|
52 | pass | |
39 | for plat in ['win32', 'win-amd64']: |
|
53 | for plat,py in plat_py.items(): | |
40 | cmd = cmd_t.format(**locals()) |
|
54 | cmd = cmd_t.format(**locals()) | |
41 | sh(cmd) |
|
55 | sh(cmd) | |
42 | orig = glob.glob(os.path.join('dist', 'ipython-*.{plat}.exe'.format(**locals())))[0] |
|
56 | orig = glob.glob(os.path.join('dist', 'ipython-*.{plat}.exe'.format(**locals())))[0] |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now