##// END OF EJS Templates
Initial reply handling implemented along with css fixes.
Brian Granger -
Show More
@@ -1,102 +1,102 b''
1 1 import signal
2 2 import sys
3 3
4 4 from IPython.zmq.ipkernel import launch_kernel
5 5 from session import SessionManager
6 6
7 7
8 8 class DuplicateKernelError(Exception):
9 9 pass
10 10
11 11
12 12 class KernelManager(object):
13 13
14 14 ip = '127.0.0.1'
15 15
16 16 def __init__(self, context):
17 17 self.context = context
18 18 self._kernels = {}
19 19
20 20 @property
21 21 def kernel_ids(self):
22 22 return self._kernels.keys()
23 23
24 24 def __len__(self):
25 25 return len(self.kernel_ids)
26 26
27 27 def __contains__(self, kernel_id):
28 28 if kernel_id in self.kernel_ids:
29 29 return True
30 30 else:
31 31 return False
32 32
33 33 def start_kernel(self, kernel_id):
34 34 if kernel_id in self._kernels:
35 35 raise DuplicateKernelError("Kernel already exists: %s" % kernel_id)
36 (process, shell_port, iopub_port, stdin_port, hb_port) = launch_kernel()
36 (process, shell_port, iopub_port, stdin_port, hb_port) = launch_kernel(pylab='inline')
37 37 d = dict(
38 38 process = process,
39 39 stdin_port = stdin_port,
40 40 iopub_port = iopub_port,
41 41 shell_port = shell_port,
42 42 hb_port = hb_port,
43 43 session_manager = SessionManager(self, kernel_id, self.context)
44 44 )
45 45 self._kernels[kernel_id] = d
46 46 return kernel_id
47 47
48 48 def kill_kernel(self, kernel_id):
49 49 kernel_process = self.get_kernel_process(kernel_id)
50 50 if kernel_process is not None:
51 51 # Attempt to kill the kernel.
52 52 try:
53 53 kernel_process.kill()
54 54 except OSError, e:
55 55 # In Windows, we will get an Access Denied error if the process
56 56 # has already terminated. Ignore it.
57 57 if not (sys.platform == 'win32' and e.winerror == 5):
58 58 raise
59 59 del self._kernels[kernel_id]
60 60
61 61 def interrupt_kernel(self, kernel_id):
62 62 kernel_process = self.get_kernel_process(kernel_id)
63 63 if kernel_process is not None:
64 64 if sys.platform == 'win32':
65 65 from parentpoller import ParentPollerWindows as Poller
66 66 Poller.send_interrupt(kernel_process.win32_interrupt_event)
67 67 else:
68 68 kernel_process.send_signal(signal.SIGINT)
69 69
70 70 def signal_kernel(self, kernel_id, signum):
71 71 """ Sends a signal to the kernel. Note that since only SIGTERM is
72 72 supported on Windows, this function is only useful on Unix systems.
73 73 """
74 74 kernel_process = self.get_kernel_process(kernel_id)
75 75 if kernel_process is not None:
76 76 kernel_process.send_signal(signum)
77 77
78 78 def get_kernel_process(self, kernel_id):
79 79 d = self._kernels.get(kernel_id)
80 80 if d is not None:
81 81 return d['process']
82 82 else:
83 83 raise KeyError("Kernel with id not found: %s" % kernel_id)
84 84
85 85 def get_kernel_ports(self, kernel_id):
86 86 d = self._kernels.get(kernel_id)
87 87 if d is not None:
88 88 dcopy = d.copy()
89 89 dcopy.pop('process')
90 90 return dcopy
91 91 else:
92 92 raise KeyError("Kernel with id not found: %s" % kernel_id)
93 93
94 94 def get_session_manager(self, kernel_id):
95 95 d = self._kernels.get(kernel_id)
96 96 if d is not None:
97 97 return d['session_manager']
98 98 else:
99 99 raise KeyError("Kernel with id not found: %s" % kernel_id)
100 100
101 101
102 102
@@ -1,133 +1,135 b''
1 1 import json
2 2 import logging
3 3 import os
4 4
5 5 import zmq
6 6
7 7 # Install the pyzmq ioloop. This has to be done before anything else from
8 8 # tornado is imported.
9 9 from zmq.eventloop import ioloop
10 10 import tornado.ioloop
11 11 tornado.ioloop = ioloop
12 12
13 13 from tornado import httpserver
14 14 from tornado import options
15 15 from tornado import web
16 16 from tornado import websocket
17 17
18 18 from kernelmanager import KernelManager
19 19
20 20 options.define("port", default=8888, help="run on the given port", type=int)
21 21
22 22 _session_id_regex = r"(?P<session_id>\w+-\w+-\w+-\w+-\w+)"
23 23 _kernel_id_regex = r"(?P<kernel_id>\w+)"
24 24
25 25
26 26 class MainHandler(web.RequestHandler):
27 27 def get(self):
28 28 self.render('notebook.html')
29 29
30 30
31 31 class BaseKernelHandler(object):
32 32
33 33 def get_kernel(self):
34 34 return self.application.kernel_manager
35 35
36 36 def get_session(self, kernel_id):
37 37 km = self.get_kernel()
38 38 sm = km.get_session_manager(kernel_id)
39 39 return sm
40 40
41 41
42 42 class KernelHandler(web.RequestHandler, BaseKernelHandler):
43 43
44 44 def get(self):
45 45 self.write(json.dumps(self.get_kernel().kernel_ids))
46 46
47 47 def post(self, *args, **kwargs):
48 48 kernel_id = kwargs['kernel_id']
49 49 self.get_kernel().start_kernel(kernel_id)
50 50 logging.info("Starting kernel: %s" % kernel_id)
51 51 self.write(json.dumps(kernel_id))
52 52
53 53
54 54 class SessionHandler(web.RequestHandler, BaseKernelHandler):
55 55
56 56 def get(self, *args, **kwargs):
57 57 kernel_id = kwargs['kernel_id']
58 58 self.write(json.dumps(self.get_session(kernel_id).session_ids))
59 59
60 60 def post(self, *args, **kwargs):
61 61 kernel_id = kwargs['kernel_id']
62 62 sm = self.get_session(kernel_id)
63 63 session_id = sm.start_session()
64 64 logging.info("Starting session: %s, %s" % (kernel_id, session_id))
65 65 self.write(json.dumps(session_id))
66 66
67 67
68 68 class ZMQStreamHandler(websocket.WebSocketHandler, BaseKernelHandler):
69 69
70 70 stream_name = ''
71 71
72 72 def open(self, *args, **kwargs):
73 73 kernel_id = kwargs['kernel_id']
74 74 session_id = kwargs['session_id']
75 75 logging.info("Connection open: %s, %s" % (kernel_id,session_id))
76 76 sm = self.get_session(kernel_id)
77 77 method_name = "get_%s_stream" % self.stream_name
78 78 method = getattr(sm, method_name)
79 79 self.zmq_stream = method(session_id)
80 80 self.zmq_stream.on_recv(self._on_zmq_reply)
81 81
82 82 def on_message(self, msg):
83 logging.info("Message received: %r" % msg)
83 logging.info("Message received: %r, %r" % (msg, self.__class__))
84 logging.info(self.zmq_stream)
84 85 self.zmq_stream.send_unicode(msg)
85 86
86 87 def on_close(self):
87 88 self.zmq_stream.close()
88 89
89 def _on_zmq_reply(self, msg):
90 logging.info("Message reply: %r" % msg)
91 self.write_message(msg)
90 def _on_zmq_reply(self, msg_list):
91 for msg in msg_list:
92 logging.info("Message reply: %r" % msg)
93 self.write_message(msg)
92 94
93 95
94 96 class IOPubStreamHandler(ZMQStreamHandler):
95 97
96 98 stream_name = 'iopub'
97 99
98 100
99 101 class ShellStreamHandler(ZMQStreamHandler):
100 102
101 103 stream_name = 'shell'
102 104
103 105
104 106 class NotebookApplication(web.Application):
105 107
106 108 def __init__(self):
107 109 handlers = [
108 110 (r"/", MainHandler),
109 111 (r"/kernels/%s" % (_kernel_id_regex,), KernelHandler),
110 112 (r"/kernels/%s/sessions" % (_kernel_id_regex,), SessionHandler),
111 113 (r"/kernels/%s/sessions/%s/iopub" % (_kernel_id_regex,_session_id_regex), IOPubStreamHandler),
112 114 (r"/kernels/%s/sessions/%s/shell" % (_kernel_id_regex,_session_id_regex), ShellStreamHandler),
113 115 ]
114 116 settings = dict(
115 117 template_path=os.path.join(os.path.dirname(__file__), "templates"),
116 118 static_path=os.path.join(os.path.dirname(__file__), "static"),
117 119 )
118 120 web.Application.__init__(self, handlers, **settings)
119 121 self.context = zmq.Context()
120 122 self.kernel_manager = KernelManager(self.context)
121 123
122 124
123 125 def main():
124 126 options.parse_command_line()
125 127 application = NotebookApplication()
126 128 http_server = httpserver.HTTPServer(application)
127 129 http_server.listen(options.options.port)
128 130 ioloop.IOLoop.instance().start()
129 131
130 132
131 133 if __name__ == "__main__":
132 134 main()
133 135
@@ -1,72 +1,73 b''
1 1 import logging
2 2 import uuid
3 3
4 4 import zmq
5 5 from zmq.eventloop.zmqstream import ZMQStream
6 6
7 7
8 8 class SessionManager(object):
9 9
10 10 def __init__(self, kernel_manager, kernel_id, context):
11 11 self.context = context
12 12 self.kernel_manager = kernel_manager
13 13 self.kernel_id = kernel_id
14 14 self._sessions = {}
15 15
16 16 def __del__(self):
17 17 self.stop_all()
18 18
19 19 @property
20 20 def session_ids(self):
21 21 return self._session.keys()
22 22
23 23 def __len__(self):
24 24 return len(self.session_ids)
25 25
26 26 def __contains__(self, session_id):
27 27 if session_id in self.session_ids:
28 28 return True
29 29 else:
30 30 return False
31 31
32 32 def start_session(self):
33 33 session_id = str(uuid.uuid4())
34 34 ports = self.kernel_manager.get_kernel_ports(self.kernel_id)
35 35 iopub_stream = self.create_connected_stream(ports['iopub_port'], zmq.SUB)
36 iopub_stream.socket.setsockopt(zmq.SUBSCRIBE, b'')
36 37 shell_stream = self.create_connected_stream(ports['shell_port'], zmq.XREQ)
37 38 self._sessions[session_id] = dict(
38 39 iopub_stream = iopub_stream,
39 40 shell_stream = shell_stream
40 41 )
41 42 return session_id
42 43
43 44 def stop_session(self, session_id):
44 45 session_dict = self._sessions.get(session_id)
45 46 if session_dict is not None:
46 47 for name, stream in session_dict.items():
47 48 stream.close()
48 49 del self._sessions[session_id]
49 50
50 51 def stop_all(self):
51 52 for session_id in self._sessions.keys():
52 53 self.stop_session(session_id)
53 54
54 55 def create_connected_stream(self, port, socket_type):
55 56 sock = self.context.socket(socket_type)
56 57 addr = "tcp://%s:%i" % (self.kernel_manager.ip, port)
57 logging.info("Connecting to: %s" % addr)
58 logging.info("Connecting to: %s, %r" % (addr, socket_type))
58 59 sock.connect(addr)
59 60 return ZMQStream(sock)
60 61
61 62 def get_stream(self, session_id, stream_name):
62 63 session_dict = self._sessions.get(session_id)
63 64 if session_dict is not None:
64 65 return session_dict[stream_name]
65 66 else:
66 67 raise KeyError("Session with id not found: %s" % session_id)
67 68
68 69 def get_iopub_stream(self, session_id):
69 70 return self.get_stream(session_id, 'iopub_stream')
70 71
71 72 def get_shell_stream(self, session_id):
72 73 return self.get_stream(session_id, 'shell_stream')
@@ -1,173 +1,217 b''
1 html, body, div, span, applet, object, iframe,
1 /**
2 * HTML5 ✰ Boilerplate
3 *
4 * style.css contains a reset, font normalization and some base styles.
5 *
6 * Credit is left where credit is due.
7 * Much inspiration was taken from these projects:
8 * - yui.yahooapis.com/2.8.1/build/base/base.css
9 * - camendesign.com/design/
10 * - praegnanz.de/weblog/htmlcssjs-kickstart
11 */
12
13
14 /**
15 * html5doctor.com Reset Stylesheet (Eric Meyer's Reset Reloaded + HTML5 baseline)
16 * v1.6.1 2010-09-17 | Authors: Eric Meyer & Richard Clark
17 * html5doctor.com/html-5-reset-stylesheet/
18 */
19
20 html, body, div, span, object, iframe,
2 21 h1, h2, h3, h4, h5, h6, p, blockquote, pre,
3 a, abbr, acronym, address, big, cite, code,
4 del, dfn, em, img, ins, kbd, q, s, samp,
5 small, strike, strong, sub, sup, tt, var,
6 b, u, i, center,
7 dl, dt, dd, ol, ul, li,
22 abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp,
23 small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li,
8 24 fieldset, form, label, legend,
9 25 table, caption, tbody, tfoot, thead, tr, th, td,
10 article, aside, canvas, details, embed,
11 figure, figcaption, footer, header, hgroup,
12 menu, nav, output, ruby, section, summary,
26 article, aside, canvas, details, figcaption, figure,
27 footer, header, hgroup, menu, nav, section, summary,
13 28 time, mark, audio, video {
14 margin: 0;
15 padding: 0;
16 border: 0;
17 font-size: 100%;
18 font: inherit;
19 vertical-align: baseline;
29 margin: 0;
30 padding: 0;
31 border: 0;
32 font-size: 100%;
33 font: inherit;
34 vertical-align: baseline;
35 }
36
37 article, aside, details, figcaption, figure,
38 footer, header, hgroup, menu, nav, section {
39 display: block;
20 40 }
21 41
42 blockquote, q { quotes: none; }
43
44 blockquote:before, blockquote:after,
45 q:before, q:after { content: ""; content: none; }
46
47 ins { background-color: #ff9; color: #000; text-decoration: none; }
48
49 mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; }
50
51 del { text-decoration: line-through; }
52
53 abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; }
54
55 table { border-collapse: collapse; border-spacing: 0; }
56
57 hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; }
58
59 input, select { vertical-align: middle; }
60
61
62 /**
63 * Font normalization inspired by YUI Library's fonts.css: developer.yahoo.com/yui/
64 */
65
66 body { font:13px/1.231 sans-serif; *font-size:small; } /* Hack retained to preserve specificity */
67 select, input, textarea, button { font:99% sans-serif; }
68
69 /* Normalize monospace sizing:
70 en.wikipedia.org/wiki/MediaWiki_talk:Common.css/Archive_11#Teletype_style_fix_for_Chrome */
71 pre, code, kbd, samp { font-family: monospace, sans-serif; }
72
73
74
22 75 body {
23 76 background-color: white;
24 77 }
25 78
26 79 span#ipython_notebook h1 {
27 80 font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
28 81 font-size: 32pt;
29 82 padding: 10px;
30 83 margin: 10px;
31 84 }
32 85
33 86
34 87 div#toolbar {
35 88 width: 100%;
36 89 height: auto;
37 90 border-bottom-width: 2px;
38 91 border-bottom-style: solid;
39 92 border-bottom-color: black;
40 padding: 5px;
93 padding: 5px;
41 94 }
42 95
43 #main_toolbar {
44 }
45 96
46 #main_toolbar button {
97 /*#main_toolbar button {
47 98 font-size: 0.9em;
48 }
99 }*/
49 100
50 101 div.notebook {
51 width: 760px;
102 width: 790px;
52 103 height: 100%;
53 104 margin-left: auto;
54 105 margin-right: auto;
55 106 padding-top: 5px;
56 107 padding-bottom: 5px;
57 108 background-color: white;
58
59 /* Uncomment this block for help in debugging the padding and margins
60 /* border-left-width: 1px;
61 border-left-style: solid;
62 border-left-color: black;
63 border-right-width: 1px;
64 border-right-style: solid;
65 border-right-color: black;
66 border-bottom-width: 1px;
67 border-bottom-style: solid;
68 border-bottom-color: black;*/
69 109 }
70 110
71 111 div.cell {
72 112 width: 740px;
73 margin: 5px 5px 5px 5px;
113 margin: 5px auto 5px 5px;
74 114 padding: 5px;
75 font-size: 11pt;
76 115 position: relative;
116 display: table;
77 117 }
78 118
119
79 120 div.code_cell {
80 121 background-color: white;
81 122 }
82 123
83 124 div.prompt {
84 125 vertical-align: top;
85 126 display: table-cell;
86 width: 85px;
87 min-width 80px !important;
127 width: 80px;
128 padding: 0px;
129 margin: 0px;
88 130 font-family: Menlo, "Courier New", Courier, mono;
89 131 font-weight: normal;
90 132 font-style: normal;
91 133 }
92 134
93 135 div.input {
94 display: table;
95 height: auto;
136 display: table-row;
137 padding: 0px;
138 margin: 0px;
96 139 }
97 140
98 141 div.input_prompt {
99 142 color: blue;
100 143 }
101 144
102 145
103 146 textarea.input_area {
104 147 text-align: left;
105 148 font-family: Menlo, "Courier New", Courier, mono;
106 149 font-size: inherit;
107 150 border-style: none;
108 151 display: table-cell;
109 margin: 0;
110 padding: 0;
152 padding: 0px;
153 margin: 0px;
111 154 overflow: auto;
112 155 font-weight: normal;
113 156 font-style: normal;
114 width: 665px;
157 width: 650px;
115 158 outline: none;
116 159 resize: none;
117 160 }
118 161
119 162
120 163 div.output {
121 display: table;
164 display: table-row;
165 padding: 0px;
166 margin: 0px;
122 167 }
123 168
124 169 div.output_prompt {
125 170 color: red;
126 171 }
127 172
128 173 div.output_area {
129 174 text-align: left;
130 175 font-family: Menlo, "Courier New", Courier, mono;
131 font-size: inherit;
132 margin: 0;
133 padding: 0;
176 padding: 0px;
177 margin: 0px;
134 178 display: table-cell;
135 width: 665px;
179 width: 650px;
136 180 }
137 181
138 182 div.text_cell {
139 183 background-color: white;
140 184 }
141 185
142 186 textarea.text_cell_input {
143 187 font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
144 188 font-size: inherit;
145 189 outline: none;
146 190 resize: none;
147 191 width: inherit;
148 192 border-style: none;
149 193 padding: 0;
150 194 margin: 0;
151 195 color: black;
152 196 }
153 197
154 198 div.text_cell_render {
155 199 font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
156 200 font-size: inherit;
157 201 outline: none;
158 202 resize: none;
159 203 width: inherit;
160 204 border-style: none;
161 205 padding: 0;
162 206 margin: 0;
163 207 color: black;
164 208 }
165 209
166 210 div.text_cell_render em {
167 211 font-style: italic;
168 212 }
169 213
170 214 div.text_cell_render strong {
171 215 font-weight: bold;
172 216 }
173 217
@@ -1,582 +1,806 b''
1 1 var IPYTHON = {};
2 2
3 3
4 4 //============================================================================
5 // Utilities
6 //============================================================================
7
8
9 var uuid = function () {
10 // http://www.ietf.org/rfc/rfc4122.txt
11 var s = [];
12 var hexDigits = "0123456789ABCDEF";
13 for (var i = 0; i < 32; i++) {
14 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
15 }
16 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
17 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
18
19 var uuid = s.join("");
20 return uuid;
21 };
22
23
24 //Fix raw text to parse correctly in crazy XML
25 function xmlencode(string) {
26 return string.replace(/\&/g,'&'+'amp;')
27 .replace(/</g,'&'+'lt;')
28 .replace(/>/g,'&'+'gt;')
29 .replace(/\'/g,'&'+'apos;')
30 .replace(/\"/g,'&'+'quot;')
31 .replace(/`/g,'&'+'#96;')
32 }
33
34 //Map from terminal commands to CSS classes
35 attrib = {
36 "30":"cblack", "31":"cred",
37 "32":"cgreen", "33":"cyellow",
38 "34":"cblue", "36":"ccyan",
39 "37":"cwhite", "01":"cbold"}
40
41 //Fixes escaped console commands, IE colors. Turns them into HTML
42 function fixConsole(txt) {
43 txt = xmlencode(txt)
44 var re = /\033\[([\d;]*?)m/
45 var opened = false
46 var cmds = []
47 var opener = ""
48 var closer = ""
49
50 while (re.test(txt)) {
51 var cmds = txt.match(re)[1].split(";")
52 closer = opened?"</span>":""
53 opened = cmds.length > 1 || cmds[0] != 0
54 var rep = []
55 for (var i in cmds)
56 if (typeof(attrib[cmds[i]]) != "undefined")
57 rep.push(attrib[cmds[i]])
58 opener = rep.length > 0?"<span class=\""+rep.join(" ")+"\">":""
59 txt = txt.replace(re, closer + opener)
60 }
61 if (opened) txt += "</span>"
62 return txt.trim()
63 }
64
65 //============================================================================
5 66 // Notebook
6 67 //============================================================================
7 68
8 69
9 70 var Notebook = function (selector) {
10 71 this.element = $(selector);
11 72 this.element.scroll();
12 73 this.element.data("notebook", this);
13 74 this.next_prompt_number = 1;
75 this.next_kernel_number = 0;
76 this.kernel = null;
77 this.msg_cell_map = {};
14 78 this.bind_events();
79 this.start_kernel();
15 80 };
16 81
17 82
18 83 Notebook.prototype.bind_events = function () {
19 84 var that = this;
20 85 $(document).keydown(function (event) {
21 console.log(event);
86 // console.log(event);
22 87 if (event.which == 38 && event.shiftKey) {
23 88 event.preventDefault();
24 89 that.select_prev();
25 90 } else if (event.which == 40 && event.shiftKey) {
26 91 event.preventDefault();
27 92 that.select_next();
28 93 } else if (event.which == 13 && event.shiftKey) {
29 94 // The focus is not quite working here.
95 var cell = that.selected_cell();
96 var cell_index = that.find_cell_index(cell);
97 if (cell instanceof CodeCell) {
98 event.preventDefault();
99 cell.clear_output();
100 var msg_id = that.kernel.execute(cell.get_code());
101 that.msg_cell_map[msg_id] = cell.cell_id;
102 if (cell_index === (that.ncells()-1)) {
103 that.insert_code_cell_after();
104 } else {
105 that.select(cell_index+1);
106 };
107 }
108 } else if (event.which == 9) {
30 109 event.preventDefault();
31 that.insert_code_cell_after();
32 }
110 var cell = that.selected_cell();
111 if (cell instanceof CodeCell) {
112 var ta = cell.element.find("textarea.input_area");
113 ta.val(ta.val() + " ");
114 };
115 };
33 116 });
34 117 };
35 118
36 119
37 120 // Cell indexing, retrieval, etc.
38 121
39 122
40 123 Notebook.prototype.cell_elements = function () {
41 124 return this.element.children("div.cell");
42 125 }
43 126
44 127
45 128 Notebook.prototype.ncells = function (cell) {
46 129 return this.cell_elements().length;
47 130 }
48 131
49 132
50 133 // TODO: we are often calling cells as cells()[i], which we should optimize
51 134 // to cells(i) or a new method.
52 135 Notebook.prototype.cells = function () {
53 136 return this.cell_elements().toArray().map(function (e) {
54 137 return $(e).data("cell");
55 138 });
56 139 }
57 140
58 141
59 142 Notebook.prototype.find_cell_index = function (cell) {
60 143 var result = null;
61 144 this.cell_elements().filter(function (index) {
62 145 if ($(this).data("cell") === cell) {
63 146 result = index;
64 147 };
65 148 });
66 149 return result;
67 150 };
68 151
69 152
70 153 Notebook.prototype.index_or_selected = function (index) {
71 154 return index || this.selected_index() || 0;
72 155 }
73 156
74 157
75 158 Notebook.prototype.select = function (index) {
76 159 if (index !== undefined && index >= 0 && index < this.ncells()) {
77 160 if (this.selected_index() !== null) {
78 161 this.selected_cell().unselect();
79 162 };
80 163 this.cells()[index].select();
81 164 };
82 165 return this;
83 166 };
84 167
85 168
86 169 Notebook.prototype.select_next = function () {
87 170 var index = this.selected_index();
88 171 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
89 172 this.select(index+1);
90 173 };
91 174 return this;
92 175 };
93 176
94 177
95 178 Notebook.prototype.select_prev = function () {
96 179 var index = this.selected_index();
97 180 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
98 181 this.select(index-1);
99 182 };
100 183 return this;
101 184 };
102 185
103 186
104 187 Notebook.prototype.selected_index = function () {
105 188 var result = null;
106 189 this.cell_elements().filter(function (index) {
107 190 if ($(this).data("cell").selected === true) {
108 191 result = index;
109 192 };
110 193 });
111 194 return result;
112 195 };
113 196
114 197
198 Notebook.prototype.cell_for_msg = function (msg_id) {
199 var cell_id = this.msg_cell_map[msg_id];
200 var result = null;
201 this.cell_elements().filter(function (index) {
202 cell = $(this).data("cell");
203 if (cell.cell_id === cell_id) {
204 result = cell;
205 };
206 });
207 return result;
208 };
209
210
115 211 Notebook.prototype.selected_cell = function () {
116 212 return this.cell_elements().eq(this.selected_index()).data("cell");
117 213 }
118 214
119 215
120 216 // Cell insertion, deletion and moving.
121 217
122 218
123 219 Notebook.prototype.delete_cell = function (index) {
124 220 var i = index || this.selected_index();
125 221 if (i !== null && i >= 0 && i < this.ncells()) {
126 222 this.cell_elements().eq(i).remove();
127 223 if (i === (this.ncells())) {
128 224 this.select(i-1);
129 225 } else {
130 226 this.select(i);
131 227 };
132 228 };
133 229 return this;
134 230 };
135 231
136 232
137 233 Notebook.prototype.append_cell = function (cell) {
138 234 this.element.append(cell.element);
139 235 return this;
140 236 };
141 237
142 238
143 239 Notebook.prototype.insert_cell_after = function (cell, index) {
144 240 var ncells = this.ncells();
145 241 if (ncells === 0) {
146 242 this.append_cell(cell);
147 243 return this;
148 244 };
149 245 if (index >= 0 && index < ncells) {
150 246 this.cell_elements().eq(index).after(cell.element);
151 247 };
152 248 return this
153 249 };
154 250
155 251
156 252 Notebook.prototype.insert_cell_before = function (cell, index) {
157 253 var ncells = this.ncells();
158 254 if (ncells === 0) {
159 255 this.append_cell(cell);
160 256 return this;
161 257 };
162 258 if (index >= 0 && index < ncells) {
163 259 this.cell_elements().eq(index).before(cell.element);
164 260 };
165 261 return this;
166 262 };
167 263
168 264
169 265 Notebook.prototype.move_cell_up = function (index) {
170 266 var i = index || this.selected_index();
171 267 if (i !== null && i < this.ncells() && i > 0) {
172 268 var pivot = this.cell_elements().eq(i-1);
173 269 var tomove = this.cell_elements().eq(i);
174 270 if (pivot !== null && tomove !== null) {
175 271 tomove.detach();
176 272 pivot.before(tomove);
177 273 this.select(i-1);
178 274 };
179 275 };
180 276 return this;
181 277 }
182 278
183 279
184 280 Notebook.prototype.move_cell_down = function (index) {
185 281 var i = index || this.selected_index();
186 282 if (i !== null && i < (this.ncells()-1) && i >= 0) {
187 283 var pivot = this.cell_elements().eq(i+1)
188 284 var tomove = this.cell_elements().eq(i)
189 285 if (pivot !== null && tomove !== null) {
190 286 tomove.detach();
191 287 pivot.after(tomove);
192 288 this.select(i+1);
193 289 };
194 290 };
195 291 return this;
196 292 }
197 293
198 294
199 295 Notebook.prototype.sort_cells = function () {
200 296 var ncells = this.ncells();
201 297 var sindex = this.selected_index();
202 298 var swapped;
203 299 do {
204 300 swapped = false
205 301 for (var i=1; i<ncells; i++) {
206 302 current = this.cell_elements().eq(i).data("cell");
207 303 previous = this.cell_elements().eq(i-1).data("cell");
208 304 if (previous.input_prompt_number > current.input_prompt_number) {
209 305 this.move_cell_up(i);
210 306 swapped = true;
211 307 };
212 308 };
213 309 } while (swapped);
214 310 this.select(sindex);
215 311 return this;
216 312 };
217 313
218 314
219 315 Notebook.prototype.insert_code_cell_before = function (index) {
220 316 // TODO: Bounds check for i
221 317 var i = this.index_or_selected(index);
222 318 var cell = new CodeCell(this);
223 319 cell.set_input_prompt(this.next_prompt_number);
224 320 this.next_prompt_number = this.next_prompt_number + 1;
225 321 this.insert_cell_before(cell, i);
226 322 this.select(this.find_cell_index(cell));
227 323 return this;
228 324 }
229 325
230 326
231 327 Notebook.prototype.insert_code_cell_after = function (index) {
232 328 // TODO: Bounds check for i
233 329 var i = this.index_or_selected(index);
234 330 var cell = new CodeCell(this);
235 331 cell.set_input_prompt(this.next_prompt_number);
236 332 this.next_prompt_number = this.next_prompt_number + 1;
237 333 this.insert_cell_after(cell, i);
238 334 this.select(this.find_cell_index(cell));
239 335 return this;
240 336 }
241 337
242 338
243 339 Notebook.prototype.insert_text_cell_before = function (index) {
244 340 // TODO: Bounds check for i
245 341 var i = this.index_or_selected(index);
246 342 var cell = new TextCell(this);
247 343 cell.config_mathjax();
248 344 this.insert_cell_before(cell, i);
249 345 this.select(this.find_cell_index(cell));
250 346 return this;
251 347 }
252 348
253 349
254 350 Notebook.prototype.insert_text_cell_after = function (index) {
255 351 // TODO: Bounds check for i
256 352 var i = this.index_or_selected(index);
257 353 var cell = new TextCell(this);
258 354 cell.config_mathjax();
259 355 this.insert_cell_after(cell, i);
260 356 this.select(this.find_cell_index(cell));
261 357 return this;
262 358 }
263 359
264 360
265 361 Notebook.prototype.text_to_code = function (index) {
266 362 // TODO: Bounds check for i
267 363 var i = this.index_or_selected(index);
268 364 var source_element = this.cell_elements().eq(i);
269 365 var source_cell = source_element.data("cell");
270 366 if (source_cell instanceof TextCell) {
271 367 this.insert_code_cell_after(i);
272 368 var target_cell = this.cells()[i+1];
273 369 var text = source_element.find("textarea.text_cell_input").val();
274 370 target_cell.element.find("textarea.input_area").val(text);
275 371 source_element.remove();
276 372 };
277 373 };
278 374
279 375
280 376 Notebook.prototype.code_to_text = function (index) {
281 377 // TODO: Bounds check for i
282 378 var i = this.index_or_selected(index);
283 379 var source_element = this.cell_elements().eq(i);
284 380 var source_cell = source_element.data("cell");
285 381 if (source_cell instanceof CodeCell) {
286 382 this.insert_text_cell_after(i);
287 383 var target_cell = this.cells()[i+1];
288 384 var text = source_element.find("textarea.input_area").val();
289 385 if (text === "") {text = target_cell.placeholder;};
290 386 target_cell.element.find("textarea.text_cell_input").val(text);
291 387 target_cell.element.find("textarea.text_cell_input").html(text);
292 388 target_cell.element.find("div.text_cell_render").html(text);
293 389
294 390 source_element.remove();
295 391 };
296 392 };
297 393
298 394
299 395 // Cell collapsing
300 396
301 397 Notebook.prototype.collapse = function (index) {
302 398 var i = this.index_or_selected(index);
303 399 this.cells()[i].collapse();
304 }
400 };
305 401
306 402
307 403 Notebook.prototype.expand = function (index) {
308 404 var i = this.index_or_selected(index);
309 405 this.cells()[i].expand();
310 }
406 };
407
408
409 // Kernel related things
410
411 Notebook.prototype.start_kernel = function () {
412 this.kernel = new Kernel("kernel" + this.next_kernel_number);
413 this.next_kernel_number = this.next_kernel_number + 1;
414 this.kernel.start_kernel(this._kernel_started, this);
415 };
416
417
418 Notebook.prototype._kernel_started = function () {
419 console.log("Kernel started: ", this.kernel.kernel_id);
420 this.kernel.start_session(this._session_started, this);
421 };
422
423
424 Notebook.prototype._session_started = function () {
425 console.log("Session started: ", this.kernel.session_id);
426 var that = this;
427
428 this.kernel.shell_channel.onmessage = function (e) {
429 reply = $.parseJSON(e.data);
430 console.log(reply);
431 var msg_type = reply.msg_type;
432 var cell = that.cell_for_msg(reply.parent_header.msg_id);
433 if (msg_type === "execute_reply") {
434 cell.set_prompt(reply.content.execution_count);
435 };
436 };
437
438 this.kernel.iopub_channel.onmessage = function (e) {
439 reply = $.parseJSON(e.data);
440 console.log(reply);
441 var msg_type = reply.msg_type;
442 var cell = that.cell_for_msg(reply.parent_header.msg_id);
443 if (msg_type === "stream") {
444 cell.expand();
445 cell.append_stream(reply.content.data + "\n");
446 } else if (msg_type === "pyout" || msg_type === "display_data") {
447 cell.expand();
448 cell.append_display_data(reply.content.data);
449 };
450 };
451 };
452
453
454 Notebook.prototype._handle_execute_reply = function (reply, cell) {
455 cell.set_prompt(reply.content.execution_count);
456 };
311 457
312 458
313 459 //============================================================================
314 460 // Cell
315 461 //============================================================================
316 462
317 463
318 464 var Cell = function (notebook) {
319 465 this.notebook = notebook;
320 466 this.selected = false;
321 467 this.element;
322 468 this.create_element();
323 469 if (this.element !== undefined) {
324 470 this.element.data("cell", this);
325 471 this.bind_events();
326 472 }
473 this.cell_id = uuid();
327 474 };
328 475
329 476
330 477 Cell.prototype.select = function () {
331 478 this.element.addClass('ui-widget-content ui-corner-all');
332 479 this.selected = true;
333 480 // TODO: we need t test across browsers to see if both of these are needed.
334 481 // In the meantime, there should not be any harm in having them both.
335 482 this.element.find('textarea').trigger('focusin');
336 483 this.element.find('textarea').trigger('focus');
337 484 };
338 485
339 486
340 487 Cell.prototype.unselect = function () {
341 488 this.element.removeClass('ui-widget-content ui-corner-all');
342 489 this.selected = false;
343 490 };
344 491
345 492
346 493 Cell.prototype.bind_events = function () {
347 494 var that = this;
348 495 var nb = that.notebook
349 496 that.element.click(function (event) {
350 497 if (that.selected === false) {
351 498 nb.select(nb.find_cell_index(that));
352 499 };
353 500 });
354 501 that.element.focusin(function (event) {
355 502 if (that.selected === false) {
356 503 nb.select(nb.find_cell_index(that));
357 504 };
358 505 });
359 506 };
360 507
361 508
362 509 // Subclasses must implement create_element.
363 510 Cell.prototype.create_element = function () {};
364 511
365 512
366 513 //============================================================================
367 514 // CodeCell
368 515 //============================================================================
369 516
370 517
371 518 var CodeCell = function (notebook) {
372 519 Cell.apply(this, arguments);
373 520 this.input_prompt_number = ' ';
374 521 this.output_prompt_number = ' ';
375 522 };
376 523
377 524
378 525 CodeCell.prototype = new Cell();
379 526
380 527
381 528 CodeCell.prototype.create_element = function () {
382 529 var cell = $('<div></div>').addClass('cell code_cell')
383 530 var input = $('<div></div>').addClass('input').append(
384 531 $('<div/>').addClass('prompt input_prompt')
385 532 ).append(
386 533 $('<textarea/>').addClass('input_area').
387 534 attr('rows',1).
388 535 attr('cols',80).
389 536 attr('wrap','hard').
390 537 autoGrow()
391 538 );
392 539 var output = $('<div></div>').addClass('output').append(
393 540 $('<div/>').addClass('prompt output_prompt')
394 541 ).append(
395 542 $('<div/>').addClass('output_area')
396 543 );
397 output.hide();
398 544 cell.append(input).append(output);
399 545 this.element = cell;
546 this.collapse()
547 };
548
549
550 CodeCell.prototype.append_stream = function (data) {
551 var data_list = data.split("\n");
552 console.log(data_list);
553 if (data_list.length > 0) {
554 for (var i=0; i<data_list.length; i++) {
555 console.log(i, data_list[i]);
556 var toinsert = fixConsole(data_list[i]);
557 this.element.find("div.output_area").append($("<p>").append(toinsert));
558 };
559 }
560 };
561
562
563 CodeCell.prototype.append_display_data = function (data) {
564 if (data["image/svg+xml"] !== undefined) {
565 this.append_svg(data["image/svg+xml"]);
566 } else if (data["text/plain"] !== undefined) {
567 console.log(data["text/plain"]);
568 this.append_stream(data["text/plain"]);
569 };
570 };
571
572 CodeCell.prototype.append_svg = function (svg) {
573 this.element.find("div.output_area").append(svg);
574 };
575
576
577 CodeCell.prototype.clear_output = function () {
578 this.element.find("div.output_area").html("");
400 579 };
401 580
402 581
403 582 CodeCell.prototype.collapse = function () {
404 583 this.element.find('div.output').hide();
405 584 };
406 585
407 586
408 587 CodeCell.prototype.expand = function () {
409 588 this.element.find('div.output').show();
410 589 };
411 590
412 591
413 592 CodeCell.prototype.set_prompt = function (number) {
414 593 this.set_input_prompt(number);
415 594 this.set_output_prompt(number);
416 595 };
417 596
418 597 CodeCell.prototype.set_input_prompt = function (number) {
419 598 var n = number || ' ';
420 599 this.input_prompt_number = n
421 600 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
422 601 };
423 602
424 603
425 604 CodeCell.prototype.set_output_prompt = function (number) {
426 605 var n = number || ' ';
427 606 this.output_prompt_number = n
428 607 this.element.find('div.output_prompt').html('Out[' + n + ']:');
429 608 };
430 609
431 610
611 CodeCell.prototype.get_code = function () {
612 return this.element.find("textarea.input_area").val();
613 };
614
432 615 //============================================================================
433 616 // TextCell
434 617 //============================================================================
435 618
436 619
437 620 var TextCell = function (notebook) {
438 621 Cell.apply(this, arguments);
439 622 this.placeholder = "Type <strong>HTML</strong> and LaTeX: $\\alpha^2$"
440 623 };
441 624
442 625
443 626 TextCell.prototype = new Cell();
444 627
445 628
446 629 TextCell.prototype.create_element = function () {
447 630 var cell = $("<div>").addClass('cell text_cell').
448 631 append(
449 632 $("<textarea>" + this.placeholder + "</textarea>").
450 633 addClass('text_cell_input').
451 634 attr('rows',1).
452 635 attr('cols',80).
453 636 autoGrow()
454 637 ).append(
455 638 $('<div></div>').addClass('text_cell_render')
456 639 )
457 640 this.element = cell;
458 641 };
459 642
460 643
461 644 TextCell.prototype.select = function () {
462 645 this.edit();
463 646 Cell.prototype.select.apply(this);
464 647 };
465 648
466 649
467 650 TextCell.prototype.edit = function () {
468 651 var text_cell = this.element;
469 652 var input = text_cell.find("textarea.text_cell_input");
470 653 var output = text_cell.find("div.text_cell_render");
471 654 output.hide();
472 655 input.show().trigger('focus');
473 656 };
474 657
475 658
476 659 TextCell.prototype.render = function () {
477 660 var text_cell = this.element;
478 661 var input = text_cell.find("textarea.text_cell_input");
479 662 var output = text_cell.find("div.text_cell_render");
480 663 var text = input.val();
481 664 if (text === "") {
482 665 text = this.placeholder;
483 666 input.val(text);
484 667 };
485 668 output.html(text)
486 669 input.html(text);
487 670 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
488 671 input.hide();
489 672 output.show();
490 673 };
491 674
492 675
493 676 TextCell.prototype.config_mathjax = function () {
494 677 var text_cell = this.element;
495 678 var that = this;
496 679 text_cell.click(function () {
497 680 that.edit();
498 681 }).focusout(function () {
499 682 that.render();
500 683 });
501 684
502 685 text_cell.trigger("focusout");
503 686 };
504 687
505 688
506 689 //============================================================================
507 690 // On document ready
508 691 //============================================================================
509 692
510 693
511 var KernelManager = function () {
512 this.kernelid = null;
513 this.baseurl = "/kernels";
694 var Kernel = function (kernel_id) {
695 this.kernel_id = kernel_id;
696 this.base_url = "/kernels";
697 this.kernel_url = this.base_url + "/" + this.kernel_id
698 this.session_id = null;
699 };
700
701
702 Kernel.prototype.get_msg = function (msg_type, content) {
703 var msg = {
704 header : {
705 msg_id : uuid(),
706 username : "bgranger",
707 session: this.session_id
708 },
709 msg_type : msg_type,
710 content : content,
711 parent_header : {}
712 };
713 return msg;
714 }
715
716 Kernel.prototype.start_kernel = function (callback, context) {
717 $.post(this.kernel_url, function () {
718 callback.call(context);
719 });
514 720 };
515 721
516 722
517 KernelManager.prototype.create_kernel = function () {
723 Kernel.prototype.start_session = function (callback, context) {
518 724 var that = this;
519 $.post(this.baseurl, function (data) {
520 that.kernelid = data;
521 }, 'json');
725 $.post(this.kernel_url + "/sessions",
726 function (session_id) {
727 that._handle_start_session(session_id, callback, context);
728 },
729 'json');
522 730 }
523 731
524 732
525 KernelManager.prototype.execute = function (code, callback) {
526 var msg = {
527 header : {msg_id : 0, username : "bgranger", session: 0},
528 msg_type : "execute_request",
529 content : {code : code}
733 Kernel.prototype._handle_start_session = function (session_id, callback, context) {
734 this.session_id = session_id;
735 this.session_url = this.kernel_url + "/sessions/" + this.session_id;
736 this._start_channels();
737 callback.call(context);
738 };
739
740
741 Kernel.prototype._start_channels = function () {
742 var ws_url = "ws://127.0.0.1:8888" + this.session_url;
743 this.shell_channel = new WebSocket(ws_url + "/shell");
744 this.iopub_channel = new WebSocket(ws_url + "/iopub");
745 }
746
747
748 Kernel.prototype.execute = function (code) {
749 var content = {
750 code : code,
751 silent : false,
752 user_variables : [],
753 user_expressions : {}
530 754 };
531 var settings = {
532 data : JSON.stringify(msg),
533 processData : false,
534 contentType : "application/json",
535 success : callback,
536 type : "POST"
537 }
538 var url = this.baseurl + "/" + this.kernelid + "/" + ""
755 var msg = this.get_msg("execute_request", content);
756
757 this.shell_channel.send(JSON.stringify(msg));
758 return msg.header.msg_id;
539 759 }
540 760
541 761
542 762 //============================================================================
543 763 // On document ready
544 764 //============================================================================
545 765
546 766
547 767 $(document).ready(function () {
548 768
549 769 MathJax.Hub.Config({
550 770 tex2jax: {
551 771 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
552 772 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
553 773 }
554 774 });
555 775
556 776 $("ul#main_menu").wijmenu({animation:{animated: "slide", duration: 100, easing: null}});
557 777 IPYTHON.notebook = new Notebook('div.notebook');
558 778 IPYTHON.notebook.insert_code_cell_after();
559 779
560 780 $("#move_cell").buttonset();
561 781 $("#move_up").button("option", "icons", {primary:"ui-icon-arrowthick-1-n"});
562 782 $("#move_up").button("option", "text", false);
563 783 $("#move_up").click(function () {IPYTHON.notebook.move_cell_up();});
564 784 $("#move_down").button("option", "icons", {primary:"ui-icon-arrowthick-1-s"});
565 785 $("#move_down").button("option", "text", false);
566 786 $("#move_down").click(function () {IPYTHON.notebook.move_cell_down();});
567 787
568 788 $("#insert_delete").buttonset();
569 789 $("#insert_cell_before").click(function () {IPYTHON.notebook.insert_code_cell_before();});
570 790 $("#insert_cell_after").click(function () {IPYTHON.notebook.insert_code_cell_after();});
571 791 $("#delete_cell").button("option", "icons", {primary:"ui-icon-closethick"});
572 792 $("#delete_cell").button("option", "text", false);
573 793 $("#delete_cell").click(function () {IPYTHON.notebook.delete_cell();});
574 794
575 795 $("#cell_type").buttonset();
576 796 $("#to_code").click(function () {IPYTHON.notebook.text_to_code();});
577 797 $("#to_text").click(function () {IPYTHON.notebook.code_to_text();});
578 798
579 799 $("#sort").buttonset();
580 800 $("#sort_cells").click(function () {IPYTHON.notebook.sort_cells();});
581 801
802 $("#toggle").buttonset();
803 $("#collapse").click(function () {IPYTHON.notebook.collapse();});
804 $("#expand").click(function () {IPYTHON.notebook.expand();});
805
582 806 }); No newline at end of file
@@ -1,84 +1,88 b''
1 1 <!DOCTYPE HTML>
2 2 <html>
3 3
4 4 <head>
5 5 <meta charset="utf-8">
6 6
7 7 <title>IPython Notebook</title>
8 8 <link rel="stylesheet" href="static/css/notebook.css" type="text/css" />
9 9 <link rel="stylesheet" href="static/jquery/css/jquery.wijmo-open.1.1.3.css" type="text/css" />
10 10 <link rel="stylesheet" href="static/jquery/css/themes/aristo/jquery-wijmo.css" type="text/css" />
11 11 <!-- <link rel="stylesheet" href="static/jquery/css/themes/ui-lightness/jquery-ui-1.8.10.custom.css" type="text/css" /> -->
12 12 <!-- <script src="static/mathjax/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript" charset="utf-8"></script> -->
13 13 <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" charset="utf-8"></script>
14 14 </head>
15 15
16 16 <body>
17 17
18 18 <div id="header">
19 19 <span id="ipython_notebook"><h1>IPython Notebook</h1></span>
20 20 </div>
21 21
22 22 <div id="tools">
23 23
24 24 <ul id="main_menu">
25 25 <li><a>Cell</a>
26 26 <ul>
27 27 <li><a>Run</a></li>
28 28 <li><a>Move up</a></li>
29 29 <li><a>Move down</a></li>
30 30 <li><a>Delete</a></li>
31 31 </ul>
32 32 </li>
33 33 <li><a>Kernel</a>
34 34 <ul>
35 35 <li><a>Interrupt</a></li>
36 36 <li><a>Restart</a></li>
37 37 </ul>
38 38 </li>
39 39 <li><a>Help</a>
40 40 <ul>
41 41 <li><a>Notebook help</a></li>
42 42 <li></li>
43 43 <li><a href="http://docs.python.org" target="_blank">Python Docs</a></li>
44 44 <li><a href="http://ipython.github.com/ipython-doc/dev/index.html" target="_blank">IPython Docs</a></li>
45 45 <li><a href="http://docs.scipy.org/doc/numpy/reference/" target="_blank">NumPy Docs</a></li>
46 46 <li><a href="http://docs.scipy.org/doc/scipy/reference/" target="_blank">SciPy Docs</a></li>
47 47 <li><a href="http://docs.sympy.org/dev/index.html" target="_blank">SymPy Docs</a></li>
48 48 </ul>
49 49 </li>
50 50 </ul>
51 51
52 52 <div id="toolbar">
53 53 <span id="main_toolbar">
54 54 <span id="move_cell">
55 55 <button id="move_up">Move up</button>
56 56 <button id="move_down">Move down</button>
57 57 </span>
58 58 <span id="insert_delete">
59 59 <button id="insert_cell_before">Before</button>
60 60 <button id="insert_cell_after">After</button>
61 61 <button id="delete_cell">Delete</button>
62 62 </span>
63 63 <span id="cell_type">
64 64 <button id="to_code">Code</button>
65 65 <button id="to_text">Text</button>
66 66 </span>
67 67 <span id="sort">
68 68 <button id="sort_cells">Sort</button>
69 69 </span>
70 <span id="toggle">
71 <button id="collapse">Collapse</button>
72 <button id="expand">Expand</button>
73 </span>
70 74 </span>
71 75 </div>
72 76
73 77 </div>
74 78
75 79 <div class="notebook"></div>
76 80
77 81 <script src="static/jquery/js/jquery-1.5.1.min.js" type="text/javascript" charset="utf-8"></script>
78 82 <script src="static/jquery/js/jquery-ui-1.8.10.custom.min.js" type="text/javascript" charset="utf-8"></script>
79 83 <script src="static/jquery/js/jquery.autogrow.js" type="text/javascript" charset="utf-8"></script>
80 84 <script src="static/jquery/js/jquery.wijmo-open.1.1.3.min.js" type="text/javascript" charset="utf-8"></script>
81 85 <script src="static/js/notebook.js" type="text/javascript" charset="utf-8"></script>
82 86 </body>
83 87
84 88 </html> No newline at end of file
@@ -1,680 +1,679 b''
1 1 #!/usr/bin/env python
2 2 """A simple interactive kernel that talks to a frontend over 0MQ.
3 3
4 4 Things to do:
5 5
6 6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 7 call set_parent on all the PUB objects with the message about to be executed.
8 8 * Implement random port and security key logic.
9 9 * Implement control messages.
10 10 * Implement event loop and poll version.
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 # Standard library imports.
19 19 import __builtin__
20 20 import atexit
21 21 import sys
22 22 import time
23 23 import traceback
24 24 import logging
25 25 # System library imports.
26 26 import zmq
27 27
28 28 # Local imports.
29 29 from IPython.config.configurable import Configurable
30 30 from IPython.config.application import boolean_flag
31 31 from IPython.core.application import ProfileDir
32 32 from IPython.core.shellapp import (
33 33 InteractiveShellApp, shell_flags, shell_aliases
34 34 )
35 35 from IPython.utils import io
36 36 from IPython.utils.jsonutil import json_clean
37 37 from IPython.lib import pylabtools
38 38 from IPython.utils.traitlets import (
39 39 List, Instance, Float, Dict, Bool, Int, Unicode, CaselessStrEnum
40 40 )
41 41
42 42 from entry_point import base_launch_kernel
43 43 from kernelapp import KernelApp, kernel_flags, kernel_aliases
44 44 from iostream import OutStream
45 45 from session import Session, Message
46 46 from zmqshell import ZMQInteractiveShell
47 47
48 48
49
50 49 #-----------------------------------------------------------------------------
51 50 # Main kernel class
52 51 #-----------------------------------------------------------------------------
53 52
54 53 class Kernel(Configurable):
55 54
56 55 #---------------------------------------------------------------------------
57 56 # Kernel interface
58 57 #---------------------------------------------------------------------------
59 58
60 59 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
61 60 session = Instance(Session)
62 61 shell_socket = Instance('zmq.Socket')
63 62 iopub_socket = Instance('zmq.Socket')
64 63 stdin_socket = Instance('zmq.Socket')
65 64 log = Instance(logging.Logger)
66 65
67 66 # Private interface
68 67
69 68 # Time to sleep after flushing the stdout/err buffers in each execute
70 69 # cycle. While this introduces a hard limit on the minimal latency of the
71 70 # execute cycle, it helps prevent output synchronization problems for
72 71 # clients.
73 72 # Units are in seconds. The minimum zmq latency on local host is probably
74 73 # ~150 microseconds, set this to 500us for now. We may need to increase it
75 74 # a little if it's not enough after more interactive testing.
76 75 _execute_sleep = Float(0.0005, config=True)
77 76
78 77 # Frequency of the kernel's event loop.
79 78 # Units are in seconds, kernel subclasses for GUI toolkits may need to
80 79 # adapt to milliseconds.
81 80 _poll_interval = Float(0.05, config=True)
82 81
83 82 # If the shutdown was requested over the network, we leave here the
84 83 # necessary reply message so it can be sent by our registered atexit
85 84 # handler. This ensures that the reply is only sent to clients truly at
86 85 # the end of our shutdown process (which happens after the underlying
87 86 # IPython shell's own shutdown).
88 87 _shutdown_message = None
89 88
90 89 # This is a dict of port number that the kernel is listening on. It is set
91 90 # by record_ports and used by connect_request.
92 91 _recorded_ports = Dict()
93 92
94 93
95 94
96 95 def __init__(self, **kwargs):
97 96 super(Kernel, self).__init__(**kwargs)
98 97
99 98 # Before we even start up the shell, register *first* our exit handlers
100 99 # so they come before the shell's
101 100 atexit.register(self._at_shutdown)
102 101
103 102 # Initialize the InteractiveShell subclass
104 103 self.shell = ZMQInteractiveShell.instance(config=self.config)
105 104 self.shell.displayhook.session = self.session
106 105 self.shell.displayhook.pub_socket = self.iopub_socket
107 106 self.shell.display_pub.session = self.session
108 107 self.shell.display_pub.pub_socket = self.iopub_socket
109 108
110 109 # TMP - hack while developing
111 110 self.shell._reply_content = None
112 111
113 112 # Build dict of handlers for message types
114 113 msg_types = [ 'execute_request', 'complete_request',
115 114 'object_info_request', 'history_request',
116 115 'connect_request', 'shutdown_request']
117 116 self.handlers = {}
118 117 for msg_type in msg_types:
119 118 self.handlers[msg_type] = getattr(self, msg_type)
120 119
121 120 def do_one_iteration(self):
122 121 """Do one iteration of the kernel's evaluation loop.
123 122 """
124 123 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
125 124 if msg is None:
126 125 return
127 126
128 127 msg_type = msg['header']['msg_type']
129 128
130 129 # This assert will raise in versions of zeromq 2.0.7 and lesser.
131 130 # We now require 2.0.8 or above, so we can uncomment for safety.
132 131 # print(ident,msg, file=sys.__stdout__)
133 132 assert ident is not None, "Missing message part."
134 133
135 134 # Print some info about this message and leave a '--->' marker, so it's
136 135 # easier to trace visually the message chain when debugging. Each
137 136 # handler prints its message at the end.
138 137 self.log.debug('\n*** MESSAGE TYPE:'+str(msg_type)+'***')
139 138 self.log.debug(' Content: '+str(msg['content'])+'\n --->\n ')
140 139
141 140 # Find and call actual handler for message
142 141 handler = self.handlers.get(msg_type, None)
143 142 if handler is None:
144 143 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
145 144 else:
146 145 handler(ident, msg)
147 146
148 147 # Check whether we should exit, in case the incoming message set the
149 148 # exit flag on
150 149 if self.shell.exit_now:
151 150 self.log.debug('\nExiting IPython kernel...')
152 151 # We do a normal, clean exit, which allows any actions registered
153 152 # via atexit (such as history saving) to take place.
154 153 sys.exit(0)
155 154
156 155
157 156 def start(self):
158 157 """ Start the kernel main loop.
159 158 """
160 159 poller = zmq.Poller()
161 160 poller.register(self.shell_socket, zmq.POLLIN)
162 161 while True:
163 162 try:
164 163 # scale by extra factor of 10, because there is no
165 164 # reason for this to be anything less than ~ 0.1s
166 165 # since it is a real poller and will respond
167 166 # to events immediately
168 167 poller.poll(10*1000*self._poll_interval)
169 168 self.do_one_iteration()
170 169 except KeyboardInterrupt:
171 170 # Ctrl-C shouldn't crash the kernel
172 171 io.raw_print("KeyboardInterrupt caught in kernel")
173 172
174 173 def record_ports(self, ports):
175 174 """Record the ports that this kernel is using.
176 175
177 176 The creator of the Kernel instance must call this methods if they
178 177 want the :meth:`connect_request` method to return the port numbers.
179 178 """
180 179 self._recorded_ports = ports
181 180
182 181 #---------------------------------------------------------------------------
183 182 # Kernel request handlers
184 183 #---------------------------------------------------------------------------
185 184
186 185 def _publish_pyin(self, code, parent):
187 186 """Publish the code request on the pyin stream."""
188 187
189 188 pyin_msg = self.session.send(self.iopub_socket, u'pyin',{u'code':code}, parent=parent)
190 189
191 190 def execute_request(self, ident, parent):
192 191
193 192 status_msg = self.session.send(self.iopub_socket,
194 193 u'status',
195 194 {u'execution_state':u'busy'},
196 195 parent=parent
197 196 )
198 197
199 198 try:
200 199 content = parent[u'content']
201 200 code = content[u'code']
202 201 silent = content[u'silent']
203 202 except:
204 203 self.log.error("Got bad msg: ")
205 204 self.log.error(str(Message(parent)))
206 205 return
207 206
208 207 shell = self.shell # we'll need this a lot here
209 208
210 209 # Replace raw_input. Note that is not sufficient to replace
211 210 # raw_input in the user namespace.
212 211 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
213 212 __builtin__.raw_input = raw_input
214 213
215 214 # Set the parent message of the display hook and out streams.
216 215 shell.displayhook.set_parent(parent)
217 216 shell.display_pub.set_parent(parent)
218 217 sys.stdout.set_parent(parent)
219 218 sys.stderr.set_parent(parent)
220 219
221 220 # Re-broadcast our input for the benefit of listening clients, and
222 221 # start computing output
223 222 if not silent:
224 223 self._publish_pyin(code, parent)
225 224
226 225 reply_content = {}
227 226 try:
228 227 if silent:
229 228 # run_code uses 'exec' mode, so no displayhook will fire, and it
230 229 # doesn't call logging or history manipulations. Print
231 230 # statements in that code will obviously still execute.
232 231 shell.run_code(code)
233 232 else:
234 233 # FIXME: the shell calls the exception handler itself.
235 234 shell.run_cell(code)
236 235 except:
237 236 status = u'error'
238 237 # FIXME: this code right now isn't being used yet by default,
239 238 # because the run_cell() call above directly fires off exception
240 239 # reporting. This code, therefore, is only active in the scenario
241 240 # where runlines itself has an unhandled exception. We need to
242 241 # uniformize this, for all exception construction to come from a
243 242 # single location in the codbase.
244 243 etype, evalue, tb = sys.exc_info()
245 244 tb_list = traceback.format_exception(etype, evalue, tb)
246 245 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
247 246 else:
248 247 status = u'ok'
249 248
250 249 reply_content[u'status'] = status
251 250
252 251 # Return the execution counter so clients can display prompts
253 252 reply_content['execution_count'] = shell.execution_count -1
254 253
255 254 # FIXME - fish exception info out of shell, possibly left there by
256 255 # runlines. We'll need to clean up this logic later.
257 256 if shell._reply_content is not None:
258 257 reply_content.update(shell._reply_content)
259 258 # reset after use
260 259 shell._reply_content = None
261 260
262 261 # At this point, we can tell whether the main code execution succeeded
263 262 # or not. If it did, we proceed to evaluate user_variables/expressions
264 263 if reply_content['status'] == 'ok':
265 264 reply_content[u'user_variables'] = \
266 265 shell.user_variables(content[u'user_variables'])
267 266 reply_content[u'user_expressions'] = \
268 267 shell.user_expressions(content[u'user_expressions'])
269 268 else:
270 269 # If there was an error, don't even try to compute variables or
271 270 # expressions
272 271 reply_content[u'user_variables'] = {}
273 272 reply_content[u'user_expressions'] = {}
274 273
275 274 # Payloads should be retrieved regardless of outcome, so we can both
276 275 # recover partial output (that could have been generated early in a
277 276 # block, before an error) and clear the payload system always.
278 277 reply_content[u'payload'] = shell.payload_manager.read_payload()
279 278 # Be agressive about clearing the payload because we don't want
280 279 # it to sit in memory until the next execute_request comes in.
281 280 shell.payload_manager.clear_payload()
282 281
283 282 # Flush output before sending the reply.
284 283 sys.stdout.flush()
285 284 sys.stderr.flush()
286 285 # FIXME: on rare occasions, the flush doesn't seem to make it to the
287 286 # clients... This seems to mitigate the problem, but we definitely need
288 287 # to better understand what's going on.
289 288 if self._execute_sleep:
290 289 time.sleep(self._execute_sleep)
291 290
292 291 # Send the reply.
293 292 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
294 293 reply_content, parent, ident=ident)
295 294 self.log.debug(str(reply_msg))
296 295
297 296 if reply_msg['content']['status'] == u'error':
298 297 self._abort_queue()
299 298
300 299 status_msg = self.session.send(self.iopub_socket,
301 300 u'status',
302 301 {u'execution_state':u'idle'},
303 302 parent=parent
304 303 )
305 304
306 305 def complete_request(self, ident, parent):
307 306 txt, matches = self._complete(parent)
308 307 matches = {'matches' : matches,
309 308 'matched_text' : txt,
310 309 'status' : 'ok'}
311 310 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
312 311 matches, parent, ident)
313 312 self.log.debug(str(completion_msg))
314 313
315 314 def object_info_request(self, ident, parent):
316 315 object_info = self.shell.object_inspect(parent['content']['oname'])
317 316 # Before we send this object over, we scrub it for JSON usage
318 317 oinfo = json_clean(object_info)
319 318 msg = self.session.send(self.shell_socket, 'object_info_reply',
320 319 oinfo, parent, ident)
321 320 self.log.debug(msg)
322 321
323 322 def history_request(self, ident, parent):
324 323 # We need to pull these out, as passing **kwargs doesn't work with
325 324 # unicode keys before Python 2.6.5.
326 325 hist_access_type = parent['content']['hist_access_type']
327 326 raw = parent['content']['raw']
328 327 output = parent['content']['output']
329 328 if hist_access_type == 'tail':
330 329 n = parent['content']['n']
331 330 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
332 331 include_latest=True)
333 332
334 333 elif hist_access_type == 'range':
335 334 session = parent['content']['session']
336 335 start = parent['content']['start']
337 336 stop = parent['content']['stop']
338 337 hist = self.shell.history_manager.get_range(session, start, stop,
339 338 raw=raw, output=output)
340 339
341 340 elif hist_access_type == 'search':
342 341 pattern = parent['content']['pattern']
343 342 hist = self.shell.history_manager.search(pattern, raw=raw, output=output)
344 343
345 344 else:
346 345 hist = []
347 346 content = {'history' : list(hist)}
348 347 msg = self.session.send(self.shell_socket, 'history_reply',
349 348 content, parent, ident)
350 349 self.log.debug(str(msg))
351 350
352 351 def connect_request(self, ident, parent):
353 352 if self._recorded_ports is not None:
354 353 content = self._recorded_ports.copy()
355 354 else:
356 355 content = {}
357 356 msg = self.session.send(self.shell_socket, 'connect_reply',
358 357 content, parent, ident)
359 358 self.log.debug(msg)
360 359
361 360 def shutdown_request(self, ident, parent):
362 361 self.shell.exit_now = True
363 362 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
364 363 sys.exit(0)
365 364
366 365 #---------------------------------------------------------------------------
367 366 # Protected interface
368 367 #---------------------------------------------------------------------------
369 368
370 369 def _abort_queue(self):
371 370 while True:
372 371 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
373 372 if msg is None:
374 373 break
375 374 else:
376 375 assert ident is not None, \
377 376 "Unexpected missing message part."
378 377
379 378 self.log.debug("Aborting:\n"+str(Message(msg)))
380 379 msg_type = msg['header']['msg_type']
381 380 reply_type = msg_type.split('_')[0] + '_reply'
382 381 reply_msg = self.session.send(self.shell_socket, reply_type,
383 382 {'status' : 'aborted'}, msg, ident=ident)
384 383 self.log.debug(reply_msg)
385 384 # We need to wait a bit for requests to come in. This can probably
386 385 # be set shorter for true asynchronous clients.
387 386 time.sleep(0.1)
388 387
389 388 def _raw_input(self, prompt, ident, parent):
390 389 # Flush output before making the request.
391 390 sys.stderr.flush()
392 391 sys.stdout.flush()
393 392
394 393 # Send the input request.
395 394 content = dict(prompt=prompt)
396 395 msg = self.session.send(self.stdin_socket, u'input_request', content, parent)
397 396
398 397 # Await a response.
399 398 ident, reply = self.session.recv(self.stdin_socket, 0)
400 399 try:
401 400 value = reply['content']['value']
402 401 except:
403 402 self.log.error("Got bad raw_input reply: ")
404 403 self.log.error(str(Message(parent)))
405 404 value = ''
406 405 return value
407 406
408 407 def _complete(self, msg):
409 408 c = msg['content']
410 409 try:
411 410 cpos = int(c['cursor_pos'])
412 411 except:
413 412 # If we don't get something that we can convert to an integer, at
414 413 # least attempt the completion guessing the cursor is at the end of
415 414 # the text, if there's any, and otherwise of the line
416 415 cpos = len(c['text'])
417 416 if cpos==0:
418 417 cpos = len(c['line'])
419 418 return self.shell.complete(c['text'], c['line'], cpos)
420 419
421 420 def _object_info(self, context):
422 421 symbol, leftover = self._symbol_from_context(context)
423 422 if symbol is not None and not leftover:
424 423 doc = getattr(symbol, '__doc__', '')
425 424 else:
426 425 doc = ''
427 426 object_info = dict(docstring = doc)
428 427 return object_info
429 428
430 429 def _symbol_from_context(self, context):
431 430 if not context:
432 431 return None, context
433 432
434 433 base_symbol_string = context[0]
435 434 symbol = self.shell.user_ns.get(base_symbol_string, None)
436 435 if symbol is None:
437 436 symbol = __builtin__.__dict__.get(base_symbol_string, None)
438 437 if symbol is None:
439 438 return None, context
440 439
441 440 context = context[1:]
442 441 for i, name in enumerate(context):
443 442 new_symbol = getattr(symbol, name, None)
444 443 if new_symbol is None:
445 444 return symbol, context[i:]
446 445 else:
447 446 symbol = new_symbol
448 447
449 448 return symbol, []
450 449
451 450 def _at_shutdown(self):
452 451 """Actions taken at shutdown by the kernel, called by python's atexit.
453 452 """
454 453 # io.rprint("Kernel at_shutdown") # dbg
455 454 if self._shutdown_message is not None:
456 455 self.session.send(self.shell_socket, self._shutdown_message)
457 456 self.session.send(self.iopub_socket, self._shutdown_message)
458 457 self.log.debug(str(self._shutdown_message))
459 458 # A very short sleep to give zmq time to flush its message buffers
460 459 # before Python truly shuts down.
461 460 time.sleep(0.01)
462 461
463 462
464 463 class QtKernel(Kernel):
465 464 """A Kernel subclass with Qt support."""
466 465
467 466 def start(self):
468 467 """Start a kernel with QtPy4 event loop integration."""
469 468
470 469 from IPython.external.qt_for_kernel import QtCore
471 470 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
472 471
473 472 self.app = get_app_qt4([" "])
474 473 self.app.setQuitOnLastWindowClosed(False)
475 474 self.timer = QtCore.QTimer()
476 475 self.timer.timeout.connect(self.do_one_iteration)
477 476 # Units for the timer are in milliseconds
478 477 self.timer.start(1000*self._poll_interval)
479 478 start_event_loop_qt4(self.app)
480 479
481 480
482 481 class WxKernel(Kernel):
483 482 """A Kernel subclass with Wx support."""
484 483
485 484 def start(self):
486 485 """Start a kernel with wx event loop support."""
487 486
488 487 import wx
489 488 from IPython.lib.guisupport import start_event_loop_wx
490 489
491 490 doi = self.do_one_iteration
492 491 # Wx uses milliseconds
493 492 poll_interval = int(1000*self._poll_interval)
494 493
495 494 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
496 495 # We make the Frame hidden when we create it in the main app below.
497 496 class TimerFrame(wx.Frame):
498 497 def __init__(self, func):
499 498 wx.Frame.__init__(self, None, -1)
500 499 self.timer = wx.Timer(self)
501 500 # Units for the timer are in milliseconds
502 501 self.timer.Start(poll_interval)
503 502 self.Bind(wx.EVT_TIMER, self.on_timer)
504 503 self.func = func
505 504
506 505 def on_timer(self, event):
507 506 self.func()
508 507
509 508 # We need a custom wx.App to create our Frame subclass that has the
510 509 # wx.Timer to drive the ZMQ event loop.
511 510 class IPWxApp(wx.App):
512 511 def OnInit(self):
513 512 self.frame = TimerFrame(doi)
514 513 self.frame.Show(False)
515 514 return True
516 515
517 516 # The redirect=False here makes sure that wx doesn't replace
518 517 # sys.stdout/stderr with its own classes.
519 518 self.app = IPWxApp(redirect=False)
520 519 start_event_loop_wx(self.app)
521 520
522 521
523 522 class TkKernel(Kernel):
524 523 """A Kernel subclass with Tk support."""
525 524
526 525 def start(self):
527 526 """Start a Tk enabled event loop."""
528 527
529 528 import Tkinter
530 529 doi = self.do_one_iteration
531 530 # Tk uses milliseconds
532 531 poll_interval = int(1000*self._poll_interval)
533 532 # For Tkinter, we create a Tk object and call its withdraw method.
534 533 class Timer(object):
535 534 def __init__(self, func):
536 535 self.app = Tkinter.Tk()
537 536 self.app.withdraw()
538 537 self.func = func
539 538
540 539 def on_timer(self):
541 540 self.func()
542 541 self.app.after(poll_interval, self.on_timer)
543 542
544 543 def start(self):
545 544 self.on_timer() # Call it once to get things going.
546 545 self.app.mainloop()
547 546
548 547 self.timer = Timer(doi)
549 548 self.timer.start()
550 549
551 550
552 551 class GTKKernel(Kernel):
553 552 """A Kernel subclass with GTK support."""
554 553
555 554 def start(self):
556 555 """Start the kernel, coordinating with the GTK event loop"""
557 556 from .gui.gtkembed import GTKEmbed
558 557
559 558 gtk_kernel = GTKEmbed(self)
560 559 gtk_kernel.start()
561 560
562 561
563 562 #-----------------------------------------------------------------------------
564 563 # Aliases and Flags for the IPKernelApp
565 564 #-----------------------------------------------------------------------------
566 565
567 566 flags = dict(kernel_flags)
568 567 flags.update(shell_flags)
569 568
570 569 addflag = lambda *args: flags.update(boolean_flag(*args))
571 570
572 571 flags['pylab'] = (
573 572 {'IPKernelApp' : {'pylab' : 'auto'}},
574 573 """Pre-load matplotlib and numpy for interactive use with
575 574 the default matplotlib backend."""
576 575 )
577 576
578 577 aliases = dict(kernel_aliases)
579 578 aliases.update(shell_aliases)
580 579
581 580 # it's possible we don't want short aliases for *all* of these:
582 581 aliases.update(dict(
583 582 pylab='IPKernelApp.pylab',
584 583 ))
585 584
586 585 #-----------------------------------------------------------------------------
587 586 # The IPKernelApp class
588 587 #-----------------------------------------------------------------------------
589 588
590 589 class IPKernelApp(KernelApp, InteractiveShellApp):
591 590 name = 'ipkernel'
592 591
593 592 aliases = Dict(aliases)
594 593 flags = Dict(flags)
595 594 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
596 595 # configurables
597 596 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
598 597 config=True,
599 598 help="""Pre-load matplotlib and numpy for interactive use,
600 599 selecting a particular matplotlib backend and loop integration.
601 600 """
602 601 )
603 602 pylab_import_all = Bool(True, config=True,
604 603 help="""If true, an 'import *' is done from numpy and pylab,
605 604 when using pylab"""
606 605 )
607 606 def initialize(self, argv=None):
608 607 super(IPKernelApp, self).initialize(argv)
609 608 self.init_shell()
610 609 self.init_extensions()
611 610 self.init_code()
612 611
613 612 def init_kernel(self):
614 613 kernel_factory = Kernel
615 614
616 615 kernel_map = {
617 616 'qt' : QtKernel,
618 617 'qt4': QtKernel,
619 618 'inline': Kernel,
620 619 'osx': TkKernel,
621 620 'wx' : WxKernel,
622 621 'tk' : TkKernel,
623 622 'gtk': GTKKernel,
624 623 }
625 624
626 625 if self.pylab:
627 626 key = None if self.pylab == 'auto' else self.pylab
628 627 gui, backend = pylabtools.find_gui_and_backend(key)
629 628 kernel_factory = kernel_map.get(gui)
630 629 if kernel_factory is None:
631 630 raise ValueError('GUI is not supported: %r' % gui)
632 631 pylabtools.activate_matplotlib(backend)
633 632
634 633 kernel = kernel_factory(config=self.config, session=self.session,
635 634 shell_socket=self.shell_socket,
636 635 iopub_socket=self.iopub_socket,
637 636 stdin_socket=self.stdin_socket,
638 637 log=self.log
639 638 )
640 639 self.kernel = kernel
641 640 kernel.record_ports(self.ports)
642 641
643 642 if self.pylab:
644 643 import_all = self.pylab_import_all
645 644 pylabtools.import_pylab(kernel.shell.user_ns, backend, import_all,
646 645 shell=kernel.shell)
647 646
648 647 def init_shell(self):
649 648 self.shell = self.kernel.shell
650 649
651 650
652 651 #-----------------------------------------------------------------------------
653 652 # Kernel main and launch functions
654 653 #-----------------------------------------------------------------------------
655 654
656 655 def launch_kernel(*args, **kwargs):
657 656 """Launches a localhost IPython kernel, binding to the specified ports.
658 657
659 658 This function simply calls entry_point.base_launch_kernel with the right first
660 659 command to start an ipkernel. See base_launch_kernel for arguments.
661 660
662 661 Returns
663 662 -------
664 663 A tuple of form:
665 664 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
666 665 where kernel_process is a Popen object and the ports are integers.
667 666 """
668 667 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
669 668 *args, **kwargs)
670 669
671 670
672 671 def main():
673 672 """Run an IPKernel as an application"""
674 673 app = IPKernelApp.instance()
675 674 app.initialize()
676 675 app.start()
677 676
678 677
679 678 if __name__ == '__main__':
680 679 main()
General Comments 0
You need to be logged in to leave comments. Login now