##// END OF EJS Templates
Merge pull request #3753 from ivanov/ctrl-c-for-kernel...
Paul Ivanov -
r11675:01322e25 merge
parent child Browse files
Show More
@@ -1,459 +1,470 b''
1 """An Application for launching a kernel
1 """An Application for launching a kernel
2
2
3 Authors
3 Authors
4 -------
4 -------
5 * MinRK
5 * MinRK
6 """
6 """
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2011 The IPython Development Team
8 # Copyright (C) 2011 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING.txt, distributed as part of this software.
11 # the file COPYING.txt, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 from __future__ import print_function
18 from __future__ import print_function
19
19
20 # Standard library imports
20 # Standard library imports
21 import atexit
21 import atexit
22 import json
22 import json
23 import os
23 import os
24 import sys
24 import sys
25 import signal
25 import signal
26
26
27 # System library imports
27 # System library imports
28 import zmq
28 import zmq
29 from zmq.eventloop import ioloop
29 from zmq.eventloop import ioloop
30 from zmq.eventloop.zmqstream import ZMQStream
30 from zmq.eventloop.zmqstream import ZMQStream
31
31
32 # IPython imports
32 # IPython imports
33 from IPython.core.ultratb import FormattedTB
33 from IPython.core.ultratb import FormattedTB
34 from IPython.core.application import (
34 from IPython.core.application import (
35 BaseIPythonApplication, base_flags, base_aliases, catch_config_error
35 BaseIPythonApplication, base_flags, base_aliases, catch_config_error
36 )
36 )
37 from IPython.core.profiledir import ProfileDir
37 from IPython.core.profiledir import ProfileDir
38 from IPython.core.shellapp import (
38 from IPython.core.shellapp import (
39 InteractiveShellApp, shell_flags, shell_aliases
39 InteractiveShellApp, shell_flags, shell_aliases
40 )
40 )
41 from IPython.utils import io
41 from IPython.utils import io
42 from IPython.utils.localinterfaces import LOCALHOST
42 from IPython.utils.localinterfaces import LOCALHOST
43 from IPython.utils.path import filefind
43 from IPython.utils.path import filefind
44 from IPython.utils.py3compat import str_to_bytes
44 from IPython.utils.py3compat import str_to_bytes
45 from IPython.utils.traitlets import (
45 from IPython.utils.traitlets import (
46 Any, Instance, Dict, Unicode, Integer, Bool, CaselessStrEnum,
46 Any, Instance, Dict, Unicode, Integer, Bool, CaselessStrEnum,
47 DottedObjectName,
47 DottedObjectName,
48 )
48 )
49 from IPython.utils.importstring import import_item
49 from IPython.utils.importstring import import_item
50 from IPython.kernel import write_connection_file
50 from IPython.kernel import write_connection_file
51
51
52 # local imports
52 # local imports
53 from heartbeat import Heartbeat
53 from heartbeat import Heartbeat
54 from ipkernel import Kernel
54 from ipkernel import Kernel
55 from parentpoller import ParentPollerUnix, ParentPollerWindows
55 from parentpoller import ParentPollerUnix, ParentPollerWindows
56 from session import (
56 from session import (
57 Session, session_flags, session_aliases, default_secure,
57 Session, session_flags, session_aliases, default_secure,
58 )
58 )
59 from zmqshell import ZMQInteractiveShell
59 from zmqshell import ZMQInteractiveShell
60
60
61 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
62 # Flags and Aliases
62 # Flags and Aliases
63 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
64
64
65 kernel_aliases = dict(base_aliases)
65 kernel_aliases = dict(base_aliases)
66 kernel_aliases.update({
66 kernel_aliases.update({
67 'ip' : 'IPKernelApp.ip',
67 'ip' : 'IPKernelApp.ip',
68 'hb' : 'IPKernelApp.hb_port',
68 'hb' : 'IPKernelApp.hb_port',
69 'shell' : 'IPKernelApp.shell_port',
69 'shell' : 'IPKernelApp.shell_port',
70 'iopub' : 'IPKernelApp.iopub_port',
70 'iopub' : 'IPKernelApp.iopub_port',
71 'stdin' : 'IPKernelApp.stdin_port',
71 'stdin' : 'IPKernelApp.stdin_port',
72 'control' : 'IPKernelApp.control_port',
72 'control' : 'IPKernelApp.control_port',
73 'f' : 'IPKernelApp.connection_file',
73 'f' : 'IPKernelApp.connection_file',
74 'parent': 'IPKernelApp.parent_handle',
74 'parent': 'IPKernelApp.parent_handle',
75 'transport': 'IPKernelApp.transport',
75 'transport': 'IPKernelApp.transport',
76 })
76 })
77 if sys.platform.startswith('win'):
77 if sys.platform.startswith('win'):
78 kernel_aliases['interrupt'] = 'IPKernelApp.interrupt'
78 kernel_aliases['interrupt'] = 'IPKernelApp.interrupt'
79
79
80 kernel_flags = dict(base_flags)
80 kernel_flags = dict(base_flags)
81 kernel_flags.update({
81 kernel_flags.update({
82 'no-stdout' : (
82 'no-stdout' : (
83 {'IPKernelApp' : {'no_stdout' : True}},
83 {'IPKernelApp' : {'no_stdout' : True}},
84 "redirect stdout to the null device"),
84 "redirect stdout to the null device"),
85 'no-stderr' : (
85 'no-stderr' : (
86 {'IPKernelApp' : {'no_stderr' : True}},
86 {'IPKernelApp' : {'no_stderr' : True}},
87 "redirect stderr to the null device"),
87 "redirect stderr to the null device"),
88 'pylab' : (
88 'pylab' : (
89 {'IPKernelApp' : {'pylab' : 'auto'}},
89 {'IPKernelApp' : {'pylab' : 'auto'}},
90 """Pre-load matplotlib and numpy for interactive use with
90 """Pre-load matplotlib and numpy for interactive use with
91 the default matplotlib backend."""),
91 the default matplotlib backend."""),
92 })
92 })
93
93
94 # inherit flags&aliases for any IPython shell apps
94 # inherit flags&aliases for any IPython shell apps
95 kernel_aliases.update(shell_aliases)
95 kernel_aliases.update(shell_aliases)
96 kernel_flags.update(shell_flags)
96 kernel_flags.update(shell_flags)
97
97
98 # inherit flags&aliases for Sessions
98 # inherit flags&aliases for Sessions
99 kernel_aliases.update(session_aliases)
99 kernel_aliases.update(session_aliases)
100 kernel_flags.update(session_flags)
100 kernel_flags.update(session_flags)
101
101
102 _ctrl_c_message = """\
103 NOTE: When using the `ipython kernel` entry point, Ctrl-C will not work.
104
105 To exit, you will have to explicitly quit this process, by either sending
106 "quit" from a client, or using Ctrl-\\ in UNIX-like environments.
107
108 To read more about this, see https://github.com/ipython/ipython/issues/2049
109
110 """
111
102 #-----------------------------------------------------------------------------
112 #-----------------------------------------------------------------------------
103 # Application class for starting an IPython Kernel
113 # Application class for starting an IPython Kernel
104 #-----------------------------------------------------------------------------
114 #-----------------------------------------------------------------------------
105
115
106 class IPKernelApp(BaseIPythonApplication, InteractiveShellApp):
116 class IPKernelApp(BaseIPythonApplication, InteractiveShellApp):
107 name='ipkernel'
117 name='ipkernel'
108 aliases = Dict(kernel_aliases)
118 aliases = Dict(kernel_aliases)
109 flags = Dict(kernel_flags)
119 flags = Dict(kernel_flags)
110 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
120 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
111 # the kernel class, as an importstring
121 # the kernel class, as an importstring
112 kernel_class = DottedObjectName('IPython.kernel.zmq.ipkernel.Kernel', config=True,
122 kernel_class = DottedObjectName('IPython.kernel.zmq.ipkernel.Kernel', config=True,
113 help="""The Kernel subclass to be used.
123 help="""The Kernel subclass to be used.
114
124
115 This should allow easy re-use of the IPKernelApp entry point
125 This should allow easy re-use of the IPKernelApp entry point
116 to configure and launch kernels other than IPython's own.
126 to configure and launch kernels other than IPython's own.
117 """)
127 """)
118 kernel = Any()
128 kernel = Any()
119 poller = Any() # don't restrict this even though current pollers are all Threads
129 poller = Any() # don't restrict this even though current pollers are all Threads
120 heartbeat = Instance(Heartbeat)
130 heartbeat = Instance(Heartbeat)
121 session = Instance('IPython.kernel.zmq.session.Session')
131 session = Instance('IPython.kernel.zmq.session.Session')
122 ports = Dict()
132 ports = Dict()
123
133
124 # ipkernel doesn't get its own config file
134 # ipkernel doesn't get its own config file
125 def _config_file_name_default(self):
135 def _config_file_name_default(self):
126 return 'ipython_config.py'
136 return 'ipython_config.py'
127
137
128 # inherit config file name from parent:
138 # inherit config file name from parent:
129 parent_appname = Unicode(config=True)
139 parent_appname = Unicode(config=True)
130 def _parent_appname_changed(self, name, old, new):
140 def _parent_appname_changed(self, name, old, new):
131 if self.config_file_specified:
141 if self.config_file_specified:
132 # it was manually specified, ignore
142 # it was manually specified, ignore
133 return
143 return
134 self.config_file_name = new.replace('-','_') + u'_config.py'
144 self.config_file_name = new.replace('-','_') + u'_config.py'
135 # don't let this count as specifying the config file
145 # don't let this count as specifying the config file
136 self.config_file_specified.remove(self.config_file_name)
146 self.config_file_specified.remove(self.config_file_name)
137
147
138 # connection info:
148 # connection info:
139 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
149 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
140 ip = Unicode(config=True,
150 ip = Unicode(config=True,
141 help="Set the IP or interface on which the kernel will listen.")
151 help="Set the IP or interface on which the kernel will listen.")
142 def _ip_default(self):
152 def _ip_default(self):
143 if self.transport == 'ipc':
153 if self.transport == 'ipc':
144 if self.connection_file:
154 if self.connection_file:
145 return os.path.splitext(self.abs_connection_file)[0] + '-ipc'
155 return os.path.splitext(self.abs_connection_file)[0] + '-ipc'
146 else:
156 else:
147 return 'kernel-ipc'
157 return 'kernel-ipc'
148 else:
158 else:
149 return LOCALHOST
159 return LOCALHOST
150 hb_port = Integer(0, config=True, help="set the heartbeat port [default: random]")
160 hb_port = Integer(0, config=True, help="set the heartbeat port [default: random]")
151 shell_port = Integer(0, config=True, help="set the shell (ROUTER) port [default: random]")
161 shell_port = Integer(0, config=True, help="set the shell (ROUTER) port [default: random]")
152 iopub_port = Integer(0, config=True, help="set the iopub (PUB) port [default: random]")
162 iopub_port = Integer(0, config=True, help="set the iopub (PUB) port [default: random]")
153 stdin_port = Integer(0, config=True, help="set the stdin (ROUTER) port [default: random]")
163 stdin_port = Integer(0, config=True, help="set the stdin (ROUTER) port [default: random]")
154 control_port = Integer(0, config=True, help="set the control (ROUTER) port [default: random]")
164 control_port = Integer(0, config=True, help="set the control (ROUTER) port [default: random]")
155 connection_file = Unicode('', config=True,
165 connection_file = Unicode('', config=True,
156 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
166 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
157
167
158 This file will contain the IP, ports, and authentication key needed to connect
168 This file will contain the IP, ports, and authentication key needed to connect
159 clients to this kernel. By default, this file will be created in the security dir
169 clients to this kernel. By default, this file will be created in the security dir
160 of the current profile, but can be specified by absolute path.
170 of the current profile, but can be specified by absolute path.
161 """)
171 """)
162 @property
172 @property
163 def abs_connection_file(self):
173 def abs_connection_file(self):
164 if os.path.basename(self.connection_file) == self.connection_file:
174 if os.path.basename(self.connection_file) == self.connection_file:
165 return os.path.join(self.profile_dir.security_dir, self.connection_file)
175 return os.path.join(self.profile_dir.security_dir, self.connection_file)
166 else:
176 else:
167 return self.connection_file
177 return self.connection_file
168
178
169
179
170 # streams, etc.
180 # streams, etc.
171 no_stdout = Bool(False, config=True, help="redirect stdout to the null device")
181 no_stdout = Bool(False, config=True, help="redirect stdout to the null device")
172 no_stderr = Bool(False, config=True, help="redirect stderr to the null device")
182 no_stderr = Bool(False, config=True, help="redirect stderr to the null device")
173 outstream_class = DottedObjectName('IPython.kernel.zmq.iostream.OutStream',
183 outstream_class = DottedObjectName('IPython.kernel.zmq.iostream.OutStream',
174 config=True, help="The importstring for the OutStream factory")
184 config=True, help="The importstring for the OutStream factory")
175 displayhook_class = DottedObjectName('IPython.kernel.zmq.displayhook.ZMQDisplayHook',
185 displayhook_class = DottedObjectName('IPython.kernel.zmq.displayhook.ZMQDisplayHook',
176 config=True, help="The importstring for the DisplayHook factory")
186 config=True, help="The importstring for the DisplayHook factory")
177
187
178 # polling
188 # polling
179 parent_handle = Integer(0, config=True,
189 parent_handle = Integer(0, config=True,
180 help="""kill this process if its parent dies. On Windows, the argument
190 help="""kill this process if its parent dies. On Windows, the argument
181 specifies the HANDLE of the parent process, otherwise it is simply boolean.
191 specifies the HANDLE of the parent process, otherwise it is simply boolean.
182 """)
192 """)
183 interrupt = Integer(0, config=True,
193 interrupt = Integer(0, config=True,
184 help="""ONLY USED ON WINDOWS
194 help="""ONLY USED ON WINDOWS
185 Interrupt this process when the parent is signaled.
195 Interrupt this process when the parent is signaled.
186 """)
196 """)
187
197
188 def init_crash_handler(self):
198 def init_crash_handler(self):
189 # Install minimal exception handling
199 # Install minimal exception handling
190 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
200 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
191 ostream=sys.__stdout__)
201 ostream=sys.__stdout__)
192
202
193 def init_poller(self):
203 def init_poller(self):
194 if sys.platform == 'win32':
204 if sys.platform == 'win32':
195 if self.interrupt or self.parent_handle:
205 if self.interrupt or self.parent_handle:
196 self.poller = ParentPollerWindows(self.interrupt, self.parent_handle)
206 self.poller = ParentPollerWindows(self.interrupt, self.parent_handle)
197 elif self.parent_handle:
207 elif self.parent_handle:
198 self.poller = ParentPollerUnix()
208 self.poller = ParentPollerUnix()
199
209
200 def _bind_socket(self, s, port):
210 def _bind_socket(self, s, port):
201 iface = '%s://%s' % (self.transport, self.ip)
211 iface = '%s://%s' % (self.transport, self.ip)
202 if self.transport == 'tcp':
212 if self.transport == 'tcp':
203 if port <= 0:
213 if port <= 0:
204 port = s.bind_to_random_port(iface)
214 port = s.bind_to_random_port(iface)
205 else:
215 else:
206 s.bind("tcp://%s:%i" % (self.ip, port))
216 s.bind("tcp://%s:%i" % (self.ip, port))
207 elif self.transport == 'ipc':
217 elif self.transport == 'ipc':
208 if port <= 0:
218 if port <= 0:
209 port = 1
219 port = 1
210 path = "%s-%i" % (self.ip, port)
220 path = "%s-%i" % (self.ip, port)
211 while os.path.exists(path):
221 while os.path.exists(path):
212 port = port + 1
222 port = port + 1
213 path = "%s-%i" % (self.ip, port)
223 path = "%s-%i" % (self.ip, port)
214 else:
224 else:
215 path = "%s-%i" % (self.ip, port)
225 path = "%s-%i" % (self.ip, port)
216 s.bind("ipc://%s" % path)
226 s.bind("ipc://%s" % path)
217 return port
227 return port
218
228
219 def load_connection_file(self):
229 def load_connection_file(self):
220 """load ip/port/hmac config from JSON connection file"""
230 """load ip/port/hmac config from JSON connection file"""
221 try:
231 try:
222 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
232 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
223 except IOError:
233 except IOError:
224 self.log.debug("Connection file not found: %s", self.connection_file)
234 self.log.debug("Connection file not found: %s", self.connection_file)
225 # This means I own it, so I will clean it up:
235 # This means I own it, so I will clean it up:
226 atexit.register(self.cleanup_connection_file)
236 atexit.register(self.cleanup_connection_file)
227 return
237 return
228 self.log.debug(u"Loading connection file %s", fname)
238 self.log.debug(u"Loading connection file %s", fname)
229 with open(fname) as f:
239 with open(fname) as f:
230 s = f.read()
240 s = f.read()
231 cfg = json.loads(s)
241 cfg = json.loads(s)
232 self.transport = cfg.get('transport', self.transport)
242 self.transport = cfg.get('transport', self.transport)
233 if self.ip == self._ip_default() and 'ip' in cfg:
243 if self.ip == self._ip_default() and 'ip' in cfg:
234 # not overridden by config or cl_args
244 # not overridden by config or cl_args
235 self.ip = cfg['ip']
245 self.ip = cfg['ip']
236 for channel in ('hb', 'shell', 'iopub', 'stdin', 'control'):
246 for channel in ('hb', 'shell', 'iopub', 'stdin', 'control'):
237 name = channel + '_port'
247 name = channel + '_port'
238 if getattr(self, name) == 0 and name in cfg:
248 if getattr(self, name) == 0 and name in cfg:
239 # not overridden by config or cl_args
249 # not overridden by config or cl_args
240 setattr(self, name, cfg[name])
250 setattr(self, name, cfg[name])
241 if 'key' in cfg:
251 if 'key' in cfg:
242 self.config.Session.key = str_to_bytes(cfg['key'])
252 self.config.Session.key = str_to_bytes(cfg['key'])
243
253
244 def write_connection_file(self):
254 def write_connection_file(self):
245 """write connection info to JSON file"""
255 """write connection info to JSON file"""
246 cf = self.abs_connection_file
256 cf = self.abs_connection_file
247 self.log.debug("Writing connection file: %s", cf)
257 self.log.debug("Writing connection file: %s", cf)
248 write_connection_file(cf, ip=self.ip, key=self.session.key, transport=self.transport,
258 write_connection_file(cf, ip=self.ip, key=self.session.key, transport=self.transport,
249 shell_port=self.shell_port, stdin_port=self.stdin_port, hb_port=self.hb_port,
259 shell_port=self.shell_port, stdin_port=self.stdin_port, hb_port=self.hb_port,
250 iopub_port=self.iopub_port, control_port=self.control_port)
260 iopub_port=self.iopub_port, control_port=self.control_port)
251
261
252 def cleanup_connection_file(self):
262 def cleanup_connection_file(self):
253 cf = self.abs_connection_file
263 cf = self.abs_connection_file
254 self.log.debug("Cleaning up connection file: %s", cf)
264 self.log.debug("Cleaning up connection file: %s", cf)
255 try:
265 try:
256 os.remove(cf)
266 os.remove(cf)
257 except (IOError, OSError):
267 except (IOError, OSError):
258 pass
268 pass
259
269
260 self.cleanup_ipc_files()
270 self.cleanup_ipc_files()
261
271
262 def cleanup_ipc_files(self):
272 def cleanup_ipc_files(self):
263 """cleanup ipc files if we wrote them"""
273 """cleanup ipc files if we wrote them"""
264 if self.transport != 'ipc':
274 if self.transport != 'ipc':
265 return
275 return
266 for port in (self.shell_port, self.iopub_port, self.stdin_port, self.hb_port, self.control_port):
276 for port in (self.shell_port, self.iopub_port, self.stdin_port, self.hb_port, self.control_port):
267 ipcfile = "%s-%i" % (self.ip, port)
277 ipcfile = "%s-%i" % (self.ip, port)
268 try:
278 try:
269 os.remove(ipcfile)
279 os.remove(ipcfile)
270 except (IOError, OSError):
280 except (IOError, OSError):
271 pass
281 pass
272
282
273 def init_connection_file(self):
283 def init_connection_file(self):
274 if not self.connection_file:
284 if not self.connection_file:
275 self.connection_file = "kernel-%s.json"%os.getpid()
285 self.connection_file = "kernel-%s.json"%os.getpid()
276 try:
286 try:
277 self.load_connection_file()
287 self.load_connection_file()
278 except Exception:
288 except Exception:
279 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
289 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
280 self.exit(1)
290 self.exit(1)
281
291
282 def init_sockets(self):
292 def init_sockets(self):
283 # Create a context, a session, and the kernel sockets.
293 # Create a context, a session, and the kernel sockets.
284 self.log.info("Starting the kernel at pid: %i", os.getpid())
294 self.log.info("Starting the kernel at pid: %i", os.getpid())
285 context = zmq.Context.instance()
295 context = zmq.Context.instance()
286 # Uncomment this to try closing the context.
296 # Uncomment this to try closing the context.
287 # atexit.register(context.term)
297 # atexit.register(context.term)
288
298
289 self.shell_socket = context.socket(zmq.ROUTER)
299 self.shell_socket = context.socket(zmq.ROUTER)
290 self.shell_port = self._bind_socket(self.shell_socket, self.shell_port)
300 self.shell_port = self._bind_socket(self.shell_socket, self.shell_port)
291 self.log.debug("shell ROUTER Channel on port: %i" % self.shell_port)
301 self.log.debug("shell ROUTER Channel on port: %i" % self.shell_port)
292
302
293 self.iopub_socket = context.socket(zmq.PUB)
303 self.iopub_socket = context.socket(zmq.PUB)
294 self.iopub_port = self._bind_socket(self.iopub_socket, self.iopub_port)
304 self.iopub_port = self._bind_socket(self.iopub_socket, self.iopub_port)
295 self.log.debug("iopub PUB Channel on port: %i" % self.iopub_port)
305 self.log.debug("iopub PUB Channel on port: %i" % self.iopub_port)
296
306
297 self.stdin_socket = context.socket(zmq.ROUTER)
307 self.stdin_socket = context.socket(zmq.ROUTER)
298 self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port)
308 self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port)
299 self.log.debug("stdin ROUTER Channel on port: %i" % self.stdin_port)
309 self.log.debug("stdin ROUTER Channel on port: %i" % self.stdin_port)
300
310
301 self.control_socket = context.socket(zmq.ROUTER)
311 self.control_socket = context.socket(zmq.ROUTER)
302 self.control_port = self._bind_socket(self.control_socket, self.control_port)
312 self.control_port = self._bind_socket(self.control_socket, self.control_port)
303 self.log.debug("control ROUTER Channel on port: %i" % self.control_port)
313 self.log.debug("control ROUTER Channel on port: %i" % self.control_port)
304
314
305 def init_heartbeat(self):
315 def init_heartbeat(self):
306 """start the heart beating"""
316 """start the heart beating"""
307 # heartbeat doesn't share context, because it mustn't be blocked
317 # heartbeat doesn't share context, because it mustn't be blocked
308 # by the GIL, which is accessed by libzmq when freeing zero-copy messages
318 # by the GIL, which is accessed by libzmq when freeing zero-copy messages
309 hb_ctx = zmq.Context()
319 hb_ctx = zmq.Context()
310 self.heartbeat = Heartbeat(hb_ctx, (self.transport, self.ip, self.hb_port))
320 self.heartbeat = Heartbeat(hb_ctx, (self.transport, self.ip, self.hb_port))
311 self.hb_port = self.heartbeat.port
321 self.hb_port = self.heartbeat.port
312 self.log.debug("Heartbeat REP Channel on port: %i" % self.hb_port)
322 self.log.debug("Heartbeat REP Channel on port: %i" % self.hb_port)
313 self.heartbeat.start()
323 self.heartbeat.start()
314
324
315 def log_connection_info(self):
325 def log_connection_info(self):
316 """display connection info, and store ports"""
326 """display connection info, and store ports"""
317 basename = os.path.basename(self.connection_file)
327 basename = os.path.basename(self.connection_file)
318 if basename == self.connection_file or \
328 if basename == self.connection_file or \
319 os.path.dirname(self.connection_file) == self.profile_dir.security_dir:
329 os.path.dirname(self.connection_file) == self.profile_dir.security_dir:
320 # use shortname
330 # use shortname
321 tail = basename
331 tail = basename
322 if self.profile != 'default':
332 if self.profile != 'default':
323 tail += " --profile %s" % self.profile
333 tail += " --profile %s" % self.profile
324 else:
334 else:
325 tail = self.connection_file
335 tail = self.connection_file
326 lines = [
336 lines = [
327 "To connect another client to this kernel, use:",
337 "To connect another client to this kernel, use:",
328 " --existing %s" % tail,
338 " --existing %s" % tail,
329 ]
339 ]
330 # log connection info
340 # log connection info
331 # info-level, so often not shown.
341 # info-level, so often not shown.
332 # frontends should use the %connect_info magic
342 # frontends should use the %connect_info magic
333 # to see the connection info
343 # to see the connection info
334 for line in lines:
344 for line in lines:
335 self.log.info(line)
345 self.log.info(line)
336 # also raw print to the terminal if no parent_handle (`ipython kernel`)
346 # also raw print to the terminal if no parent_handle (`ipython kernel`)
337 if not self.parent_handle:
347 if not self.parent_handle:
348 io.rprint(_ctrl_c_message)
338 for line in lines:
349 for line in lines:
339 io.rprint(line)
350 io.rprint(line)
340
351
341 self.ports = dict(shell=self.shell_port, iopub=self.iopub_port,
352 self.ports = dict(shell=self.shell_port, iopub=self.iopub_port,
342 stdin=self.stdin_port, hb=self.hb_port,
353 stdin=self.stdin_port, hb=self.hb_port,
343 control=self.control_port)
354 control=self.control_port)
344
355
345 def init_session(self):
356 def init_session(self):
346 """create our session object"""
357 """create our session object"""
347 default_secure(self.config)
358 default_secure(self.config)
348 self.session = Session(parent=self, username=u'kernel')
359 self.session = Session(parent=self, username=u'kernel')
349
360
350 def init_blackhole(self):
361 def init_blackhole(self):
351 """redirects stdout/stderr to devnull if necessary"""
362 """redirects stdout/stderr to devnull if necessary"""
352 if self.no_stdout or self.no_stderr:
363 if self.no_stdout or self.no_stderr:
353 blackhole = open(os.devnull, 'w')
364 blackhole = open(os.devnull, 'w')
354 if self.no_stdout:
365 if self.no_stdout:
355 sys.stdout = sys.__stdout__ = blackhole
366 sys.stdout = sys.__stdout__ = blackhole
356 if self.no_stderr:
367 if self.no_stderr:
357 sys.stderr = sys.__stderr__ = blackhole
368 sys.stderr = sys.__stderr__ = blackhole
358
369
359 def init_io(self):
370 def init_io(self):
360 """Redirect input streams and set a display hook."""
371 """Redirect input streams and set a display hook."""
361 if self.outstream_class:
372 if self.outstream_class:
362 outstream_factory = import_item(str(self.outstream_class))
373 outstream_factory = import_item(str(self.outstream_class))
363 sys.stdout = outstream_factory(self.session, self.iopub_socket, u'stdout')
374 sys.stdout = outstream_factory(self.session, self.iopub_socket, u'stdout')
364 sys.stderr = outstream_factory(self.session, self.iopub_socket, u'stderr')
375 sys.stderr = outstream_factory(self.session, self.iopub_socket, u'stderr')
365 if self.displayhook_class:
376 if self.displayhook_class:
366 displayhook_factory = import_item(str(self.displayhook_class))
377 displayhook_factory = import_item(str(self.displayhook_class))
367 sys.displayhook = displayhook_factory(self.session, self.iopub_socket)
378 sys.displayhook = displayhook_factory(self.session, self.iopub_socket)
368
379
369 def init_signal(self):
380 def init_signal(self):
370 signal.signal(signal.SIGINT, signal.SIG_IGN)
381 signal.signal(signal.SIGINT, signal.SIG_IGN)
371
382
372 def init_kernel(self):
383 def init_kernel(self):
373 """Create the Kernel object itself"""
384 """Create the Kernel object itself"""
374 shell_stream = ZMQStream(self.shell_socket)
385 shell_stream = ZMQStream(self.shell_socket)
375 control_stream = ZMQStream(self.control_socket)
386 control_stream = ZMQStream(self.control_socket)
376
387
377 kernel_factory = import_item(str(self.kernel_class))
388 kernel_factory = import_item(str(self.kernel_class))
378
389
379 kernel = kernel_factory(parent=self, session=self.session,
390 kernel = kernel_factory(parent=self, session=self.session,
380 shell_streams=[shell_stream, control_stream],
391 shell_streams=[shell_stream, control_stream],
381 iopub_socket=self.iopub_socket,
392 iopub_socket=self.iopub_socket,
382 stdin_socket=self.stdin_socket,
393 stdin_socket=self.stdin_socket,
383 log=self.log,
394 log=self.log,
384 profile_dir=self.profile_dir,
395 profile_dir=self.profile_dir,
385 )
396 )
386 kernel.record_ports(self.ports)
397 kernel.record_ports(self.ports)
387 self.kernel = kernel
398 self.kernel = kernel
388
399
389 def init_gui_pylab(self):
400 def init_gui_pylab(self):
390 """Enable GUI event loop integration, taking pylab into account."""
401 """Enable GUI event loop integration, taking pylab into account."""
391
402
392 # Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab`
403 # Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab`
393 # to ensure that any exception is printed straight to stderr.
404 # to ensure that any exception is printed straight to stderr.
394 # Normally _showtraceback associates the reply with an execution,
405 # Normally _showtraceback associates the reply with an execution,
395 # which means frontends will never draw it, as this exception
406 # which means frontends will never draw it, as this exception
396 # is not associated with any execute request.
407 # is not associated with any execute request.
397
408
398 shell = self.shell
409 shell = self.shell
399 _showtraceback = shell._showtraceback
410 _showtraceback = shell._showtraceback
400 try:
411 try:
401 # replace pyerr-sending traceback with stderr
412 # replace pyerr-sending traceback with stderr
402 def print_tb(etype, evalue, stb):
413 def print_tb(etype, evalue, stb):
403 print ("GUI event loop or pylab initialization failed",
414 print ("GUI event loop or pylab initialization failed",
404 file=io.stderr)
415 file=io.stderr)
405 print (shell.InteractiveTB.stb2text(stb), file=io.stderr)
416 print (shell.InteractiveTB.stb2text(stb), file=io.stderr)
406 shell._showtraceback = print_tb
417 shell._showtraceback = print_tb
407 InteractiveShellApp.init_gui_pylab(self)
418 InteractiveShellApp.init_gui_pylab(self)
408 finally:
419 finally:
409 shell._showtraceback = _showtraceback
420 shell._showtraceback = _showtraceback
410
421
411 def init_shell(self):
422 def init_shell(self):
412 self.shell = self.kernel.shell
423 self.shell = self.kernel.shell
413 self.shell.configurables.append(self)
424 self.shell.configurables.append(self)
414
425
415 @catch_config_error
426 @catch_config_error
416 def initialize(self, argv=None):
427 def initialize(self, argv=None):
417 super(IPKernelApp, self).initialize(argv)
428 super(IPKernelApp, self).initialize(argv)
418 self.init_blackhole()
429 self.init_blackhole()
419 self.init_connection_file()
430 self.init_connection_file()
420 self.init_session()
431 self.init_session()
421 self.init_poller()
432 self.init_poller()
422 self.init_sockets()
433 self.init_sockets()
423 self.init_heartbeat()
434 self.init_heartbeat()
424 # writing/displaying connection info must be *after* init_sockets/heartbeat
435 # writing/displaying connection info must be *after* init_sockets/heartbeat
425 self.log_connection_info()
436 self.log_connection_info()
426 self.write_connection_file()
437 self.write_connection_file()
427 self.init_io()
438 self.init_io()
428 self.init_signal()
439 self.init_signal()
429 self.init_kernel()
440 self.init_kernel()
430 # shell init steps
441 # shell init steps
431 self.init_path()
442 self.init_path()
432 self.init_shell()
443 self.init_shell()
433 self.init_gui_pylab()
444 self.init_gui_pylab()
434 self.init_extensions()
445 self.init_extensions()
435 self.init_code()
446 self.init_code()
436 # flush stdout/stderr, so that anything written to these streams during
447 # flush stdout/stderr, so that anything written to these streams during
437 # initialization do not get associated with the first execution request
448 # initialization do not get associated with the first execution request
438 sys.stdout.flush()
449 sys.stdout.flush()
439 sys.stderr.flush()
450 sys.stderr.flush()
440
451
441 def start(self):
452 def start(self):
442 if self.poller is not None:
453 if self.poller is not None:
443 self.poller.start()
454 self.poller.start()
444 self.kernel.start()
455 self.kernel.start()
445 try:
456 try:
446 ioloop.IOLoop.instance().start()
457 ioloop.IOLoop.instance().start()
447 except KeyboardInterrupt:
458 except KeyboardInterrupt:
448 pass
459 pass
449
460
450
461
451 def main():
462 def main():
452 """Run an IPKernel as an application"""
463 """Run an IPKernel as an application"""
453 app = IPKernelApp.instance()
464 app = IPKernelApp.instance()
454 app.initialize()
465 app.initialize()
455 app.start()
466 app.start()
456
467
457
468
458 if __name__ == '__main__':
469 if __name__ == '__main__':
459 main()
470 main()
General Comments 0
You need to be logged in to leave comments. Login now