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