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