##// END OF EJS Templates
Work to adapt routers to new Session message protocol.
Brian E. Granger -
Show More
@@ -1,163 +1,116 b''
1 """Tornado handlers for the notebook."""
2
3 #-----------------------------------------------------------------------------
4 # Imports
5 #-----------------------------------------------------------------------------
6
1 import datetime
7 import datetime
2 import json
8 import json
3 import logging
9 import logging
4 import os
10 import os
5 import urllib
11 import urllib
6 import uuid
7 from Queue import Queue
8
12
9 from tornado import web
13 from tornado import web
10 from tornado import websocket
14 from tornado import websocket
11
15
16 #-----------------------------------------------------------------------------
17 # Handlers
18 #-----------------------------------------------------------------------------
12
19
13 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
14 _kernel_action_regex = r"(?P<action>restart|interrupt)"
15
20
16 class MainHandler(web.RequestHandler):
21 class MainHandler(web.RequestHandler):
17 def get(self):
22 def get(self):
18 self.render('notebook.html')
23 self.render('notebook.html')
19
24
20
25
21 class KernelHandler(web.RequestHandler):
26 class KernelHandler(web.RequestHandler):
22
27
23 def get(self):
28 def get(self):
24 self.write(json.dumps(self.application.kernel_ids))
29 self.write(json.dumps(self.application.kernel_ids))
25
30
26 def post(self):
31 def post(self):
27 kernel_id = self.application.start_kernel()
32 kernel_id = self.application.start_kernel()
28 self.write(json.dumps(kernel_id))
33 self.write(json.dumps(kernel_id))
29
34
30
35
31 class KernelActionHandler(web.RequestHandler):
36 class KernelActionHandler(web.RequestHandler):
32
37
33 def post(self, kernel_id, action):
38 def post(self, kernel_id, action):
34 # TODO: figure out a better way of handling RPC style calls.
39 # TODO: figure out a better way of handling RPC style calls.
35 if action == 'interrupt':
40 if action == 'interrupt':
36 self.application.interrupt_kernel(kernel_id)
41 self.application.interrupt_kernel(kernel_id)
37 if action == 'restart':
42 if action == 'restart':
38 new_kernel_id = self.application.restart_kernel(kernel_id)
43 new_kernel_id = self.application.restart_kernel(kernel_id)
39 self.write(json.dumps(new_kernel_id))
44 self.write(json.dumps(new_kernel_id))
40
45
41
46
42 class ZMQStreamRouter(object):
43
44 def __init__(self, zmq_stream):
45 self.zmq_stream = zmq_stream
46 self._clients = {}
47 self.zmq_stream.on_recv(self._on_zmq_reply)
48
49 def register_client(self, client):
50 client_id = uuid.uuid4()
51 self._clients[client_id] = client
52 return client_id
53
54 def unregister_client(self, client_id):
55 del self._clients[client_id]
56
57 def copy_clients(self, router):
58 # Copy the clients of another router.
59 for client_id, client in router._clients.items():
60 client.router = self
61 self._clients[client_id] = client
62
63
64 class IOPubStreamRouter(ZMQStreamRouter):
65
66 def _on_zmq_reply(self, msg_list):
67 for client_id, client in self._clients.items():
68 for msg in msg_list:
69 client.write_message(msg)
70
71 def forward_unicode(self, client_id, msg):
72 # This is a SUB stream that we should never write to.
73 pass
74
75
76 class ShellStreamRouter(ZMQStreamRouter):
77
78 def __init__(self, zmq_stream):
79 ZMQStreamRouter.__init__(self, zmq_stream)
80 self._request_queue = Queue()
81
82 def _on_zmq_reply(self, msg_list):
83 client_id = self._request_queue.get(block=False)
84 client = self._clients.get(client_id)
85 if client is not None:
86 for msg in msg_list:
87 client.write_message(msg)
88
89 def forward_unicode(self, client_id, msg):
90 self._request_queue.put(client_id)
91 self.zmq_stream.send_unicode(msg)
92
93
94 class ZMQStreamHandler(websocket.WebSocketHandler):
47 class ZMQStreamHandler(websocket.WebSocketHandler):
95
48
96 def initialize(self, stream_name):
49 def initialize(self, stream_name):
97 self.stream_name = stream_name
50 self.stream_name = stream_name
98
51
99 def open(self, kernel_id):
52 def open(self, kernel_id):
100 self.router = self.application.get_router(kernel_id, self.stream_name)
53 self.router = self.application.get_router(kernel_id, self.stream_name)
101 self.client_id = self.router.register_client(self)
54 self.client_id = self.router.register_client(self)
102 logging.info("Connection open: %s, %s" % (kernel_id, self.client_id))
55 logging.info("Connection open: %s, %s" % (kernel_id, self.client_id))
103
56
104 def on_message(self, msg):
57 def on_message(self, msg):
105 self.router.forward_unicode(self.client_id, msg)
58 self.router.forward_unicode(self.client_id, msg)
106
59
107 def on_close(self):
60 def on_close(self):
108 self.router.unregister_client(self.client_id)
61 self.router.unregister_client(self.client_id)
109 logging.info("Connection closed: %s" % self.client_id)
62 logging.info("Connection closed: %s" % self.client_id)
110
63
111
64
112 class NotebookRootHandler(web.RequestHandler):
65 class NotebookRootHandler(web.RequestHandler):
113
66
114 def get(self):
67 def get(self):
115 files = os.listdir(os.getcwd())
68 files = os.listdir(os.getcwd())
116 files = [file for file in files if file.endswith(".ipynb")]
69 files = [file for file in files if file.endswith(".ipynb")]
117 self.write(json.dumps(files))
70 self.write(json.dumps(files))
118
71
119
72
120 class NotebookHandler(web.RequestHandler):
73 class NotebookHandler(web.RequestHandler):
121
74
122 SUPPORTED_METHODS = ("GET", "DELETE", "PUT")
75 SUPPORTED_METHODS = ("GET", "DELETE", "PUT")
123
76
124 def find_path(self, filename):
77 def find_path(self, filename):
125 filename = urllib.unquote(filename)
78 filename = urllib.unquote(filename)
126 if not filename.endswith('.ipynb'):
79 if not filename.endswith('.ipynb'):
127 raise web.HTTPError(400)
80 raise web.HTTPError(400)
128 path = os.path.join(os.getcwd(), filename)
81 path = os.path.join(os.getcwd(), filename)
129 return path
82 return path
130
83
131 def get(self, filename):
84 def get(self, filename):
132 path = self.find_path(filename)
85 path = self.find_path(filename)
133 if not os.path.isfile(path):
86 if not os.path.isfile(path):
134 raise web.HTTPError(404)
87 raise web.HTTPError(404)
135 info = os.stat(path)
88 info = os.stat(path)
136 self.set_header("Content-Type", "application/unknown")
89 self.set_header("Content-Type", "application/unknown")
137 self.set_header("Last-Modified", datetime.datetime.utcfromtimestamp(
90 self.set_header("Last-Modified", datetime.datetime.utcfromtimestamp(
138 info.st_mtime))
91 info.st_mtime))
139 f = open(path, "r")
92 f = open(path, "r")
140 try:
93 try:
141 self.finish(f.read())
94 self.finish(f.read())
142 finally:
95 finally:
143 f.close()
96 f.close()
144
97
145 def put(self, filename):
98 def put(self, filename):
146 path = self.find_path(filename)
99 path = self.find_path(filename)
147 f = open(path, "w")
100 f = open(path, "w")
148 f.write(self.request.body)
101 f.write(self.request.body)
149 f.close()
102 f.close()
150 self.finish()
103 self.finish()
151
104
152 def delete(self, filename):
105 def delete(self, filename):
153 path = self.find_path(filename)
106 path = self.find_path(filename)
154 if not os.path.isfile(path):
107 if not os.path.isfile(path):
155 raise web.HTTPError(404)
108 raise web.HTTPError(404)
156 os.unlink(path)
109 os.unlink(path)
157 self.set_status(204)
110 self.set_status(204)
158 self.finish()
111 self.finish()
159
112
160
113
161
114
162
115
163
116
@@ -1,182 +1,181 b''
1 """A kernel manager for multiple kernels."""
1 """A kernel manager for multiple kernels."""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Imports
4 # Imports
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6
6
7 import logging
8 import signal
7 import signal
9 import sys
8 import sys
10 import uuid
9 import uuid
11
10
12 import zmq
11 import zmq
13
12
14 from IPython.config.configurable import LoggingConfigurable
13 from IPython.config.configurable import LoggingConfigurable
15 from IPython.zmq.ipkernel import launch_kernel
14 from IPython.zmq.ipkernel import launch_kernel
16 from IPython.utils.traitlets import Instance, Dict, Unicode
15 from IPython.utils.traitlets import Instance, Dict
17
16
18 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
19 # Classes
18 # Classes
20 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
21
20
22 class DuplicateKernelError(Exception):
21 class DuplicateKernelError(Exception):
23 pass
22 pass
24
23
25
24
26 class KernelManager(LoggingConfigurable):
25 class KernelManager(LoggingConfigurable):
27 """A class for managing multiple kernels."""
26 """A class for managing multiple kernels."""
28
27
29 context = Instance('zmq.Context')
28 context = Instance('zmq.Context')
30 def _context_default(self):
29 def _context_default(self):
31 return zmq.Context.instance()
30 return zmq.Context.instance()
32
31
33 _kernels = Dict()
32 _kernels = Dict()
34
33
35 @property
34 @property
36 def kernel_ids(self):
35 def kernel_ids(self):
37 """Return a list of the kernel ids of the active kernels."""
36 """Return a list of the kernel ids of the active kernels."""
38 return self._kernels.keys()
37 return self._kernels.keys()
39
38
40 def __len__(self):
39 def __len__(self):
41 """Return the number of running kernels."""
40 """Return the number of running kernels."""
42 return len(self.kernel_ids)
41 return len(self.kernel_ids)
43
42
44 def __contains__(self, kernel_id):
43 def __contains__(self, kernel_id):
45 if kernel_id in self.kernel_ids:
44 if kernel_id in self.kernel_ids:
46 return True
45 return True
47 else:
46 else:
48 return False
47 return False
49
48
50 def start_kernel(self, **kwargs):
49 def start_kernel(self, **kwargs):
51 """Start a new kernel."""
50 """Start a new kernel."""
52 kernel_id = str(uuid.uuid4())
51 kernel_id = str(uuid.uuid4())
53 (process, shell_port, iopub_port, stdin_port, hb_port) = launch_kernel(**kwargs)
52 (process, shell_port, iopub_port, stdin_port, hb_port) = launch_kernel(**kwargs)
54 # Store the information for contacting the kernel. This assumes the kernel is
53 # Store the information for contacting the kernel. This assumes the kernel is
55 # running on localhost.
54 # running on localhost.
56 d = dict(
55 d = dict(
57 process = process,
56 process = process,
58 stdin_port = stdin_port,
57 stdin_port = stdin_port,
59 iopub_port = iopub_port,
58 iopub_port = iopub_port,
60 shell_port = shell_port,
59 shell_port = shell_port,
61 hb_port = hb_port,
60 hb_port = hb_port,
62 ip = '127.0.0.1'
61 ip = '127.0.0.1'
63 )
62 )
64 self._kernels[kernel_id] = d
63 self._kernels[kernel_id] = d
65 return kernel_id
64 return kernel_id
66
65
67 def kill_kernel(self, kernel_id):
66 def kill_kernel(self, kernel_id):
68 """Kill a kernel by its kernel uuid.
67 """Kill a kernel by its kernel uuid.
69
68
70 Parameters
69 Parameters
71 ==========
70 ==========
72 kernel_id : uuid
71 kernel_id : uuid
73 The id of the kernel to kill.
72 The id of the kernel to kill.
74 """
73 """
75 kernel_process = self.get_kernel_process(kernel_id)
74 kernel_process = self.get_kernel_process(kernel_id)
76 if kernel_process is not None:
75 if kernel_process is not None:
77 # Attempt to kill the kernel.
76 # Attempt to kill the kernel.
78 try:
77 try:
79 kernel_process.kill()
78 kernel_process.kill()
80 except OSError, e:
79 except OSError, e:
81 # In Windows, we will get an Access Denied error if the process
80 # In Windows, we will get an Access Denied error if the process
82 # has already terminated. Ignore it.
81 # has already terminated. Ignore it.
83 if not (sys.platform == 'win32' and e.winerror == 5):
82 if not (sys.platform == 'win32' and e.winerror == 5):
84 raise
83 raise
85 del self._kernels[kernel_id]
84 del self._kernels[kernel_id]
86
85
87 def interrupt_kernel(self, kernel_id):
86 def interrupt_kernel(self, kernel_id):
88 """Interrupt (SIGINT) the kernel by its uuid.
87 """Interrupt (SIGINT) the kernel by its uuid.
89
88
90 Parameters
89 Parameters
91 ==========
90 ==========
92 kernel_id : uuid
91 kernel_id : uuid
93 The id of the kernel to interrupt.
92 The id of the kernel to interrupt.
94 """
93 """
95 kernel_process = self.get_kernel_process(kernel_id)
94 kernel_process = self.get_kernel_process(kernel_id)
96 if kernel_process is not None:
95 if kernel_process is not None:
97 if sys.platform == 'win32':
96 if sys.platform == 'win32':
98 from parentpoller import ParentPollerWindows as Poller
97 from parentpoller import ParentPollerWindows as Poller
99 Poller.send_interrupt(kernel_process.win32_interrupt_event)
98 Poller.send_interrupt(kernel_process.win32_interrupt_event)
100 else:
99 else:
101 kernel_process.send_signal(signal.SIGINT)
100 kernel_process.send_signal(signal.SIGINT)
102
101
103 def signal_kernel(self, kernel_id, signum):
102 def signal_kernel(self, kernel_id, signum):
104 """ Sends a signal to the kernel by its uuid.
103 """ Sends a signal to the kernel by its uuid.
105
104
106 Note that since only SIGTERM is supported on Windows, this function
105 Note that since only SIGTERM is supported on Windows, this function
107 is only useful on Unix systems.
106 is only useful on Unix systems.
108
107
109 Parameters
108 Parameters
110 ==========
109 ==========
111 kernel_id : uuid
110 kernel_id : uuid
112 The id of the kernel to signal.
111 The id of the kernel to signal.
113 """
112 """
114 kernel_process = self.get_kernel_process(kernel_id)
113 kernel_process = self.get_kernel_process(kernel_id)
115 if kernel_process is not None:
114 if kernel_process is not None:
116 kernel_process.send_signal(signum)
115 kernel_process.send_signal(signum)
117
116
118 def get_kernel_process(self, kernel_id):
117 def get_kernel_process(self, kernel_id):
119 """Get the process object for a kernel by its uuid.
118 """Get the process object for a kernel by its uuid.
120
119
121 Parameters
120 Parameters
122 ==========
121 ==========
123 kernel_id : uuid
122 kernel_id : uuid
124 The id of the kernel.
123 The id of the kernel.
125 """
124 """
126 d = self._kernels.get(kernel_id)
125 d = self._kernels.get(kernel_id)
127 if d is not None:
126 if d is not None:
128 return d['process']
127 return d['process']
129 else:
128 else:
130 raise KeyError("Kernel with id not found: %s" % kernel_id)
129 raise KeyError("Kernel with id not found: %s" % kernel_id)
131
130
132 def get_kernel_ports(self, kernel_id):
131 def get_kernel_ports(self, kernel_id):
133 """Return a dictionary of ports for a kernel.
132 """Return a dictionary of ports for a kernel.
134
133
135 Parameters
134 Parameters
136 ==========
135 ==========
137 kernel_id : uuid
136 kernel_id : uuid
138 The id of the kernel.
137 The id of the kernel.
139
138
140 Returns
139 Returns
141 =======
140 =======
142 port_dict : dict
141 port_dict : dict
143 A dict of key, value pairs where the keys are the names
142 A dict of key, value pairs where the keys are the names
144 (stdin_port,iopub_port,shell_port) and the values are the
143 (stdin_port,iopub_port,shell_port) and the values are the
145 integer port numbers for those channels.
144 integer port numbers for those channels.
146 """
145 """
147 d = self._kernels.get(kernel_id)
146 d = self._kernels.get(kernel_id)
148 if d is not None:
147 if d is not None:
149 dcopy = d.copy()
148 dcopy = d.copy()
150 dcopy.pop('process')
149 dcopy.pop('process')
151 dcopy.pop('ip')
150 dcopy.pop('ip')
152 return dcopy
151 return dcopy
153 else:
152 else:
154 raise KeyError("Kernel with id not found: %s" % kernel_id)
153 raise KeyError("Kernel with id not found: %s" % kernel_id)
155
154
156 def get_kernel_ip(self, kernel_id):
155 def get_kernel_ip(self, kernel_id):
157 """Return ip address for a kernel.
156 """Return ip address for a kernel.
158
157
159 Parameters
158 Parameters
160 ==========
159 ==========
161 kernel_id : uuid
160 kernel_id : uuid
162 The id of the kernel.
161 The id of the kernel.
163
162
164 Returns
163 Returns
165 =======
164 =======
166 ip : str
165 ip : str
167 The ip address of the kernel.
166 The ip address of the kernel.
168 """
167 """
169 d = self._kernels.get(kernel_id)
168 d = self._kernels.get(kernel_id)
170 if d is not None:
169 if d is not None:
171 return d['ip']
170 return d['ip']
172 else:
171 else:
173 raise KeyError("Kernel with id not found: %s" % kernel_id)
172 raise KeyError("Kernel with id not found: %s" % kernel_id)
174
173
175 def create_session_manager(self, kernel_id):
174 def create_session_manager(self, kernel_id):
176 """Create a new session manager for a kernel by its uuid."""
175 """Create a new session manager for a kernel by its uuid."""
177 from sessionmanager import SessionManager
176 from sessionmanager import SessionManager
178 return SessionManager(
177 return SessionManager(
179 kernel_id=kernel_id, kernel_manager=self,
178 kernel_id=kernel_id, kernel_manager=self,
180 config=self.config, context=self.context, log=self.log
179 config=self.config, context=self.context, log=self.log
181 )
180 )
182
181
@@ -1,247 +1,249 b''
1 """A tornado based IPython notebook server."""
1 """A tornado based IPython notebook server."""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Imports
4 # Imports
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6
6
7 import logging
7 import logging
8 import os
8 import os
9 import signal
9 import signal
10 import sys
10 import sys
11
11
12 import zmq
12 import zmq
13
13
14 # Install the pyzmq ioloop. This has to be done before anything else from
14 # Install the pyzmq ioloop. This has to be done before anything else from
15 # tornado is imported.
15 # tornado is imported.
16 from zmq.eventloop import ioloop
16 from zmq.eventloop import ioloop
17 import tornado.ioloop
17 import tornado.ioloop
18 tornado.ioloop = ioloop
18 tornado.ioloop = ioloop
19
19
20 from tornado import httpserver
20 from tornado import httpserver
21 from tornado import web
21 from tornado import web
22
22
23 from kernelmanager import KernelManager
23 from kernelmanager import KernelManager
24 from sessionmanager import SessionManager
24 from sessionmanager import SessionManager
25 from handlers import (
25 from handlers import (
26 MainHandler, KernelHandler, KernelActionHandler, ZMQStreamHandler,
26 MainHandler, KernelHandler, KernelActionHandler, ZMQStreamHandler,
27 NotebookRootHandler, NotebookHandler
27 NotebookRootHandler, NotebookHandler
28 )
28 )
29 from routers import IOPubStreamRouter, ShellStreamRouter
29 from routers import IOPubStreamRouter, ShellStreamRouter
30
30
31 from IPython.core.application import BaseIPythonApplication
31 from IPython.core.application import BaseIPythonApplication
32 from IPython.core.profiledir import ProfileDir
32 from IPython.core.profiledir import ProfileDir
33 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
33 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
34 from IPython.zmq.session import Session
34 from IPython.zmq.session import Session
35 from IPython.zmq.zmqshell import ZMQInteractiveShell
35 from IPython.zmq.zmqshell import ZMQInteractiveShell
36 from IPython.zmq.ipkernel import (
36 from IPython.zmq.ipkernel import (
37 flags as ipkernel_flags,
37 flags as ipkernel_flags,
38 aliases as ipkernel_aliases,
38 aliases as ipkernel_aliases,
39 IPKernelApp
39 IPKernelApp
40 )
40 )
41 from IPython.utils.traitlets import Dict, Unicode, Int, Any, List, Enum
41 from IPython.utils.traitlets import Dict, Unicode, Int, Any, List, Enum
42
42
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44 # Module globals
44 # Module globals
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46
46
47 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
47 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
48 _kernel_action_regex = r"(?P<action>restart|interrupt)"
48 _kernel_action_regex = r"(?P<action>restart|interrupt)"
49
49
50 LOCALHOST = '127.0.0.1'
50 LOCALHOST = '127.0.0.1'
51
51
52 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
53 # The Tornado web application
53 # The Tornado web application
54 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
55
55
56 class NotebookWebApplication(web.Application):
56 class NotebookWebApplication(web.Application):
57
57
58 def __init__(self, kernel_manager, log, kernel_argv):
58 def __init__(self, kernel_manager, log, kernel_argv):
59 handlers = [
59 handlers = [
60 (r"/", MainHandler),
60 (r"/", MainHandler),
61 (r"/kernels", KernelHandler),
61 (r"/kernels", KernelHandler),
62 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
62 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
63 (r"/kernels/%s/iopub" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='iopub')),
63 (r"/kernels/%s/iopub" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='iopub')),
64 (r"/kernels/%s/shell" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='shell')),
64 (r"/kernels/%s/shell" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='shell')),
65 (r"/notebooks", NotebookRootHandler),
65 (r"/notebooks", NotebookRootHandler),
66 (r"/notebooks/([^/]+)", NotebookHandler)
66 (r"/notebooks/([^/]+)", NotebookHandler)
67 ]
67 ]
68 settings = dict(
68 settings = dict(
69 template_path=os.path.join(os.path.dirname(__file__), "templates"),
69 template_path=os.path.join(os.path.dirname(__file__), "templates"),
70 static_path=os.path.join(os.path.dirname(__file__), "static"),
70 static_path=os.path.join(os.path.dirname(__file__), "static"),
71 )
71 )
72 web.Application.__init__(self, handlers, **settings)
72 web.Application.__init__(self, handlers, **settings)
73
73
74 self.kernel_manager = kernel_manager
74 self.kernel_manager = kernel_manager
75 self.log = log
75 self.log = log
76 self.kernel_argv = kernel_argv
76 self.kernel_argv = kernel_argv
77 self._routers = {}
77 self._routers = {}
78 self._session_dict = {}
78 self._session_dict = {}
79
79
80 #-------------------------------------------------------------------------
80 #-------------------------------------------------------------------------
81 # Methods for managing kernels and sessions
81 # Methods for managing kernels and sessions
82 #-------------------------------------------------------------------------
82 #-------------------------------------------------------------------------
83
83
84 @property
84 @property
85 def kernel_ids(self):
85 def kernel_ids(self):
86 return self.kernel_manager.kernel_ids
86 return self.kernel_manager.kernel_ids
87
87
88 def start_kernel(self):
88 def start_kernel(self):
89 kwargs = dict()
89 kwargs = dict()
90 kwargs['extra_arguments'] = self.kernel_argv
90 kwargs['extra_arguments'] = self.kernel_argv
91 kernel_id = self.kernel_manager.start_kernel(**kwargs)
91 kernel_id = self.kernel_manager.start_kernel(**kwargs)
92 self.log.info("Kernel started: %s" % kernel_id)
92 self.log.info("Kernel started: %s" % kernel_id)
93 self.log.debug("Kernel args: %r" % kwargs)
93 self.log.debug("Kernel args: %r" % kwargs)
94 self.start_session_manager(kernel_id)
94 self.start_session_manager(kernel_id)
95 return kernel_id
95 return kernel_id
96
96
97 def start_session_manager(self, kernel_id):
97 def start_session_manager(self, kernel_id):
98 sm = self.kernel_manager.create_session_manager(kernel_id)
98 sm = self.kernel_manager.create_session_manager(kernel_id)
99 self._session_dict[kernel_id] = sm
99 self._session_dict[kernel_id] = sm
100 iopub_stream = sm.get_iopub_stream()
100 iopub_stream = sm.get_iopub_stream()
101 shell_stream = sm.get_shell_stream()
101 shell_stream = sm.get_shell_stream()
102 iopub_router = IOPubStreamRouter(iopub_stream)
102 iopub_router = IOPubStreamRouter(iopub_stream, sm.session)
103 shell_router = ShellStreamRouter(shell_stream)
103 shell_router = ShellStreamRouter(shell_stream, sm.session)
104 self._routers[(kernel_id, 'iopub')] = iopub_router
104 self._routers[(kernel_id, 'iopub')] = iopub_router
105 self._routers[(kernel_id, 'shell')] = shell_router
105 self._routers[(kernel_id, 'shell')] = shell_router
106
106
107 def kill_kernel(self, kernel_id):
107 def kill_kernel(self, kernel_id):
108 sm = self._session_dict.pop(kernel_id)
108 sm = self._session_dict.pop(kernel_id)
109 sm.stop()
109 sm.stop()
110 self.kernel_manager.kill_kernel(kernel_id)
110 self.kernel_manager.kill_kernel(kernel_id)
111 self.log.info("Kernel killed: %s" % kernel_id)
111 self.log.info("Kernel killed: %s" % kernel_id)
112
112
113 def interrupt_kernel(self, kernel_id):
113 def interrupt_kernel(self, kernel_id):
114 self.kernel_manager.interrupt_kernel(kernel_id)
114 self.kernel_manager.interrupt_kernel(kernel_id)
115 self.log.debug("Kernel interrupted: %s" % kernel_id)
115 self.log.debug("Kernel interrupted: %s" % kernel_id)
116
116
117 def restart_kernel(self, kernel_id):
117 def restart_kernel(self, kernel_id):
118 # Create the new kernel first so we can move the clients over.
118 # Create the new kernel first so we can move the clients over.
119 new_kernel_id = self.start_kernel()
119 new_kernel_id = self.start_kernel()
120
120
121 # Copy the clients over to the new routers.
121 # Copy the clients over to the new routers.
122 old_iopub_router = self.get_router(kernel_id, 'iopub')
122 old_iopub_router = self.get_router(kernel_id, 'iopub')
123 old_shell_router = self.get_router(kernel_id, 'shell')
123 old_shell_router = self.get_router(kernel_id, 'shell')
124 new_iopub_router = self.get_router(new_kernel_id, 'iopub')
124 new_iopub_router = self.get_router(new_kernel_id, 'iopub')
125 new_shell_router = self.get_router(new_kernel_id, 'shell')
125 new_shell_router = self.get_router(new_kernel_id, 'shell')
126 new_iopub_router.copy_clients(old_iopub_router)
126 new_iopub_router.copy_clients(old_iopub_router)
127 new_shell_router.copy_clients(old_shell_router)
127 new_shell_router.copy_clients(old_shell_router)
128
128
129 # Now shutdown the old session and the kernel.
129 # Now shutdown the old session and the kernel.
130 # TODO: This causes a hard crash in ZMQStream.close, which sets
130 # TODO: This causes a hard crash in ZMQStream.close, which sets
131 # self.socket to None to hastily. We will need to fix this in PyZMQ
131 # self.socket to None to hastily. We will need to fix this in PyZMQ
132 # itself. For now, we just leave the old kernel running :(
132 # itself. For now, we just leave the old kernel running :(
133 # self.kill_kernel(kernel_id)
133 # self.kill_kernel(kernel_id)
134
134
135 self.log.debug("Kernel restarted: %s -> %s" % (kernel_id, new_kernel_id))
135 self.log.debug("Kernel restarted: %s -> %s" % (kernel_id, new_kernel_id))
136 return new_kernel_id
136 return new_kernel_id
137
137
138 def get_router(self, kernel_id, stream_name):
138 def get_router(self, kernel_id, stream_name):
139 router = self._routers[(kernel_id, stream_name)]
139 router = self._routers[(kernel_id, stream_name)]
140 return router
140 return router
141
141
142
143
142 #-----------------------------------------------------------------------------
144 #-----------------------------------------------------------------------------
143 # Aliases and Flags
145 # Aliases and Flags
144 #-----------------------------------------------------------------------------
146 #-----------------------------------------------------------------------------
145
147
146 flags = dict(ipkernel_flags)
148 flags = dict(ipkernel_flags)
147
149
148 # the flags that are specific to the frontend
150 # the flags that are specific to the frontend
149 # these must be scrubbed before being passed to the kernel,
151 # these must be scrubbed before being passed to the kernel,
150 # or it will raise an error on unrecognized flags
152 # or it will raise an error on unrecognized flags
151 notebook_flags = []
153 notebook_flags = []
152
154
153 aliases = dict(ipkernel_aliases)
155 aliases = dict(ipkernel_aliases)
154
156
155 aliases.update(dict(
157 aliases.update(dict(
156 ip = 'IPythonNotebookApp.ip',
158 ip = 'IPythonNotebookApp.ip',
157 port = 'IPythonNotebookApp.port',
159 port = 'IPythonNotebookApp.port',
158 colors = 'ZMQInteractiveShell.colors',
160 colors = 'ZMQInteractiveShell.colors',
159 editor = 'RichIPythonWidget.editor',
161 editor = 'RichIPythonWidget.editor',
160 ))
162 ))
161
163
162 #-----------------------------------------------------------------------------
164 #-----------------------------------------------------------------------------
163 # IPythonNotebookApp
165 # IPythonNotebookApp
164 #-----------------------------------------------------------------------------
166 #-----------------------------------------------------------------------------
165
167
166 class IPythonNotebookApp(BaseIPythonApplication):
168 class IPythonNotebookApp(BaseIPythonApplication):
167 name = 'ipython-notebook'
169 name = 'ipython-notebook'
168 default_config_file_name='ipython_notebook_config.py'
170 default_config_file_name='ipython_notebook_config.py'
169
171
170 description = """
172 description = """
171 The IPython HTML Notebook.
173 The IPython HTML Notebook.
172
174
173 This launches a Tornado based HTML Notebook Server that serves up an
175 This launches a Tornado based HTML Notebook Server that serves up an
174 HTML5/Javascript Notebook client.
176 HTML5/Javascript Notebook client.
175 """
177 """
176
178
177 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
179 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
178 KernelManager, SessionManager, RichIPythonWidget]
180 KernelManager, SessionManager, RichIPythonWidget]
179 flags = Dict(flags)
181 flags = Dict(flags)
180 aliases = Dict(aliases)
182 aliases = Dict(aliases)
181
183
182 kernel_argv = List(Unicode)
184 kernel_argv = List(Unicode)
183
185
184 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
186 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
185 default_value=logging.INFO,
187 default_value=logging.INFO,
186 config=True,
188 config=True,
187 help="Set the log level by value or name.")
189 help="Set the log level by value or name.")
188
190
189 # connection info:
191 # connection info:
190 ip = Unicode(LOCALHOST, config=True,
192 ip = Unicode(LOCALHOST, config=True,
191 help="The IP address the notebook server will listen on."
193 help="The IP address the notebook server will listen on."
192 )
194 )
193
195
194 port = Int(8888, config=True,
196 port = Int(8888, config=True,
195 help="The port the notebook server will listen on."
197 help="The port the notebook server will listen on."
196 )
198 )
197
199
198 # the factory for creating a widget
200 # the factory for creating a widget
199 widget_factory = Any(RichIPythonWidget)
201 widget_factory = Any(RichIPythonWidget)
200
202
201 def parse_command_line(self, argv=None):
203 def parse_command_line(self, argv=None):
202 super(IPythonNotebookApp, self).parse_command_line(argv)
204 super(IPythonNotebookApp, self).parse_command_line(argv)
203 if argv is None:
205 if argv is None:
204 argv = sys.argv[1:]
206 argv = sys.argv[1:]
205
207
206 self.kernel_argv = list(argv) # copy
208 self.kernel_argv = list(argv) # copy
207 # kernel should inherit default config file from frontend
209 # kernel should inherit default config file from frontend
208 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
210 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
209 # scrub frontend-specific flags
211 # scrub frontend-specific flags
210 for a in argv:
212 for a in argv:
211 if a.startswith('-') and a.lstrip('-') in notebook_flags:
213 if a.startswith('-') and a.lstrip('-') in notebook_flags:
212 self.kernel_argv.remove(a)
214 self.kernel_argv.remove(a)
213
215
214 def init_kernel_manager(self):
216 def init_kernel_manager(self):
215 # Don't let Qt or ZMQ swallow KeyboardInterupts.
217 # Don't let Qt or ZMQ swallow KeyboardInterupts.
216 signal.signal(signal.SIGINT, signal.SIG_DFL)
218 signal.signal(signal.SIGINT, signal.SIG_DFL)
217
219
218 # Create a KernelManager and start a kernel.
220 # Create a KernelManager and start a kernel.
219 self.kernel_manager = KernelManager(config=self.config, log=self.log)
221 self.kernel_manager = KernelManager(config=self.config, log=self.log)
220
222
221 def init_logging(self):
223 def init_logging(self):
222 super(IPythonNotebookApp, self).init_logging()
224 super(IPythonNotebookApp, self).init_logging()
223 # This prevents double log messages because tornado use a root logger that
225 # This prevents double log messages because tornado use a root logger that
224 # self.log is a child of. The logging module dipatches log messages to a log
226 # self.log is a child of. The logging module dipatches log messages to a log
225 # and all of its ancenstors until propagate is set to False.
227 # and all of its ancenstors until propagate is set to False.
226 self.log.propagate = False
228 self.log.propagate = False
227
229
228 def initialize(self, argv=None):
230 def initialize(self, argv=None):
229 super(IPythonNotebookApp, self).initialize(argv)
231 super(IPythonNotebookApp, self).initialize(argv)
230 self.init_kernel_manager()
232 self.init_kernel_manager()
231 self.web_app = NotebookWebApplication(self.kernel_manager, self.log, self.kernel_argv)
233 self.web_app = NotebookWebApplication(self.kernel_manager, self.log, self.kernel_argv)
232 self.http_server = httpserver.HTTPServer(self.web_app)
234 self.http_server = httpserver.HTTPServer(self.web_app)
233 self.http_server.listen(self.port)
235 self.http_server.listen(self.port)
234
236
235 def start(self):
237 def start(self):
236 self.log.info("The IPython Notebook is running at: http://%s:%i" % (self.ip, self.port))
238 self.log.info("The IPython Notebook is running at: http://%s:%i" % (self.ip, self.port))
237 ioloop.IOLoop.instance().start()
239 ioloop.IOLoop.instance().start()
238
240
239 #-----------------------------------------------------------------------------
241 #-----------------------------------------------------------------------------
240 # Main entry point
242 # Main entry point
241 #-----------------------------------------------------------------------------
243 #-----------------------------------------------------------------------------
242
244
243 def launch_new_instance():
245 def launch_new_instance():
244 app = IPythonNotebookApp()
246 app = IPythonNotebookApp()
245 app.initialize()
247 app.initialize()
246 app.start()
248 app.start()
247
249
@@ -1,57 +1,58 b''
1 import uuid
1 import uuid
2 from Queue import Queue
2 from Queue import Queue
3
3 import json
4
4
5 class ZMQStreamRouter(object):
5 class ZMQStreamRouter(object):
6
6
7 def __init__(self, zmq_stream):
7 def __init__(self, zmq_stream, session):
8 self.zmq_stream = zmq_stream
8 self.zmq_stream = zmq_stream
9 self.session = session
9 self._clients = {}
10 self._clients = {}
10 self.zmq_stream.on_recv(self._on_zmq_reply)
11 self.zmq_stream.on_recv(self._on_zmq_reply)
11
12
12 def register_client(self, client):
13 def register_client(self, client):
13 client_id = uuid.uuid4()
14 client_id = uuid.uuid4()
14 self._clients[client_id] = client
15 self._clients[client_id] = client
15 return client_id
16 return client_id
16
17
17 def unregister_client(self, client_id):
18 def unregister_client(self, client_id):
18 del self._clients[client_id]
19 del self._clients[client_id]
19
20
20 def copy_clients(self, router):
21 def copy_clients(self, router):
21 # Copy the clients of another router.
22 # Copy the clients of another router.
22 for client_id, client in router._clients.items():
23 for client_id, client in router._clients.items():
23 client.router = self
24 client.router = self
24 self._clients[client_id] = client
25 self._clients[client_id] = client
25
26
26
27
27 class IOPubStreamRouter(ZMQStreamRouter):
28 class IOPubStreamRouter(ZMQStreamRouter):
28
29
29 def _on_zmq_reply(self, msg_list):
30 def _on_zmq_reply(self, msg_list):
30 for client_id, client in self._clients.items():
31 for client_id, client in self._clients.items():
31 for msg in msg_list:
32 for msg in msg_list:
33 print "Got message: ", msg
32 client.write_message(msg)
34 client.write_message(msg)
33
35
34 def forward_unicode(self, client_id, msg):
36 def forward_unicode(self, client_id, msg):
35 # This is a SUB stream that we should never write to.
37 # This is a SUB stream that we should never write to.
36 pass
38 pass
37
39
38
40
39 class ShellStreamRouter(ZMQStreamRouter):
41 class ShellStreamRouter(ZMQStreamRouter):
40
42
41 def __init__(self, zmq_stream):
43 def __init__(self, zmq_stream, session):
42 ZMQStreamRouter.__init__(self, zmq_stream)
44 ZMQStreamRouter.__init__(self, zmq_stream, session)
43 self._request_queue = Queue()
45 self._request_queue = Queue()
44
46
45 def _on_zmq_reply(self, msg_list):
47 def _on_zmq_reply(self, msg_list):
46 client_id = self._request_queue.get(block=False)
48 client_id = self._request_queue.get(block=False)
47 client = self._clients.get(client_id)
49 client = self._clients.get(client_id)
48 if client is not None:
50 if client is not None:
49 for msg in msg_list:
51 for msg in msg_list:
50 client.write_message(msg)
52 client.write_message(msg)
51
53
52 def forward_unicode(self, client_id, msg):
54 def forward_unicode(self, client_id, msg):
55 print "Inbound message: ", msg
53 self._request_queue.put(client_id)
56 self._request_queue.put(client_id)
54 self.zmq_stream.send_unicode(msg)
57 self.session.send(self.zmq_stream, msg)
55
56
57
58
General Comments 0
You need to be logged in to leave comments. Login now