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