##// END OF EJS Templates
Fix IPython.start_kernel()...
Thomas Kluyver -
Show More
@@ -1,470 +1,471 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 = """\
102 _ctrl_c_message = """\
103 NOTE: When using the `ipython kernel` entry point, Ctrl-C will not work.
103 NOTE: When using the `ipython kernel` entry point, Ctrl-C will not work.
104
104
105 To exit, you will have to explicitly quit this process, by either sending
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.
106 "quit" from a client, or using Ctrl-\\ in UNIX-like environments.
107
107
108 To read more about this, see https://github.com/ipython/ipython/issues/2049
108 To read more about this, see https://github.com/ipython/ipython/issues/2049
109
109
110 """
110 """
111
111
112 #-----------------------------------------------------------------------------
112 #-----------------------------------------------------------------------------
113 # Application class for starting an IPython Kernel
113 # Application class for starting an IPython Kernel
114 #-----------------------------------------------------------------------------
114 #-----------------------------------------------------------------------------
115
115
116 class IPKernelApp(BaseIPythonApplication, InteractiveShellApp):
116 class IPKernelApp(BaseIPythonApplication, InteractiveShellApp):
117 name='ipkernel'
117 name='ipkernel'
118 aliases = Dict(kernel_aliases)
118 aliases = Dict(kernel_aliases)
119 flags = Dict(kernel_flags)
119 flags = Dict(kernel_flags)
120 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
120 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
121 # the kernel class, as an importstring
121 # the kernel class, as an importstring
122 kernel_class = DottedObjectName('IPython.kernel.zmq.ipkernel.Kernel', config=True,
122 kernel_class = DottedObjectName('IPython.kernel.zmq.ipkernel.Kernel', config=True,
123 help="""The Kernel subclass to be used.
123 help="""The Kernel subclass to be used.
124
124
125 This should allow easy re-use of the IPKernelApp entry point
125 This should allow easy re-use of the IPKernelApp entry point
126 to configure and launch kernels other than IPython's own.
126 to configure and launch kernels other than IPython's own.
127 """)
127 """)
128 kernel = Any()
128 kernel = Any()
129 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
130 heartbeat = Instance(Heartbeat)
130 heartbeat = Instance(Heartbeat)
131 session = Instance('IPython.kernel.zmq.session.Session')
131 session = Instance('IPython.kernel.zmq.session.Session')
132 ports = Dict()
132 ports = Dict()
133
133
134 # ipkernel doesn't get its own config file
134 # ipkernel doesn't get its own config file
135 def _config_file_name_default(self):
135 def _config_file_name_default(self):
136 return 'ipython_config.py'
136 return 'ipython_config.py'
137
137
138 # inherit config file name from parent:
138 # inherit config file name from parent:
139 parent_appname = Unicode(config=True)
139 parent_appname = Unicode(config=True)
140 def _parent_appname_changed(self, name, old, new):
140 def _parent_appname_changed(self, name, old, new):
141 if self.config_file_specified:
141 if self.config_file_specified:
142 # it was manually specified, ignore
142 # it was manually specified, ignore
143 return
143 return
144 self.config_file_name = new.replace('-','_') + u'_config.py'
144 self.config_file_name = new.replace('-','_') + u'_config.py'
145 # don't let this count as specifying the config file
145 # don't let this count as specifying the config file
146 self.config_file_specified.remove(self.config_file_name)
146 self.config_file_specified.remove(self.config_file_name)
147
147
148 # connection info:
148 # connection info:
149 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
149 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
150 ip = Unicode(config=True,
150 ip = Unicode(config=True,
151 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.")
152 def _ip_default(self):
152 def _ip_default(self):
153 if self.transport == 'ipc':
153 if self.transport == 'ipc':
154 if self.connection_file:
154 if self.connection_file:
155 return os.path.splitext(self.abs_connection_file)[0] + '-ipc'
155 return os.path.splitext(self.abs_connection_file)[0] + '-ipc'
156 else:
156 else:
157 return 'kernel-ipc'
157 return 'kernel-ipc'
158 else:
158 else:
159 return LOCALHOST
159 return LOCALHOST
160 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]")
161 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]")
162 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]")
163 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]")
164 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]")
165 connection_file = Unicode('', config=True,
165 connection_file = Unicode('', config=True,
166 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]
167
167
168 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
169 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
170 of the current profile, but can be specified by absolute path.
170 of the current profile, but can be specified by absolute path.
171 """)
171 """)
172 @property
172 @property
173 def abs_connection_file(self):
173 def abs_connection_file(self):
174 if os.path.basename(self.connection_file) == self.connection_file:
174 if os.path.basename(self.connection_file) == self.connection_file:
175 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)
176 else:
176 else:
177 return self.connection_file
177 return self.connection_file
178
178
179
179
180 # streams, etc.
180 # streams, etc.
181 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")
182 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")
183 outstream_class = DottedObjectName('IPython.kernel.zmq.iostream.OutStream',
183 outstream_class = DottedObjectName('IPython.kernel.zmq.iostream.OutStream',
184 config=True, help="The importstring for the OutStream factory")
184 config=True, help="The importstring for the OutStream factory")
185 displayhook_class = DottedObjectName('IPython.kernel.zmq.displayhook.ZMQDisplayHook',
185 displayhook_class = DottedObjectName('IPython.kernel.zmq.displayhook.ZMQDisplayHook',
186 config=True, help="The importstring for the DisplayHook factory")
186 config=True, help="The importstring for the DisplayHook factory")
187
187
188 # polling
188 # polling
189 parent_handle = Integer(0, config=True,
189 parent_handle = Integer(0, config=True,
190 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
191 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.
192 """)
192 """)
193 interrupt = Integer(0, config=True,
193 interrupt = Integer(0, config=True,
194 help="""ONLY USED ON WINDOWS
194 help="""ONLY USED ON WINDOWS
195 Interrupt this process when the parent is signaled.
195 Interrupt this process when the parent is signaled.
196 """)
196 """)
197
197
198 def init_crash_handler(self):
198 def init_crash_handler(self):
199 # Install minimal exception handling
199 # Install minimal exception handling
200 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
200 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
201 ostream=sys.__stdout__)
201 ostream=sys.__stdout__)
202
202
203 def init_poller(self):
203 def init_poller(self):
204 if sys.platform == 'win32':
204 if sys.platform == 'win32':
205 if self.interrupt or self.parent_handle:
205 if self.interrupt or self.parent_handle:
206 self.poller = ParentPollerWindows(self.interrupt, self.parent_handle)
206 self.poller = ParentPollerWindows(self.interrupt, self.parent_handle)
207 elif self.parent_handle:
207 elif self.parent_handle:
208 self.poller = ParentPollerUnix()
208 self.poller = ParentPollerUnix()
209
209
210 def _bind_socket(self, s, port):
210 def _bind_socket(self, s, port):
211 iface = '%s://%s' % (self.transport, self.ip)
211 iface = '%s://%s' % (self.transport, self.ip)
212 if self.transport == 'tcp':
212 if self.transport == 'tcp':
213 if port <= 0:
213 if port <= 0:
214 port = s.bind_to_random_port(iface)
214 port = s.bind_to_random_port(iface)
215 else:
215 else:
216 s.bind("tcp://%s:%i" % (self.ip, port))
216 s.bind("tcp://%s:%i" % (self.ip, port))
217 elif self.transport == 'ipc':
217 elif self.transport == 'ipc':
218 if port <= 0:
218 if port <= 0:
219 port = 1
219 port = 1
220 path = "%s-%i" % (self.ip, port)
220 path = "%s-%i" % (self.ip, port)
221 while os.path.exists(path):
221 while os.path.exists(path):
222 port = port + 1
222 port = port + 1
223 path = "%s-%i" % (self.ip, port)
223 path = "%s-%i" % (self.ip, port)
224 else:
224 else:
225 path = "%s-%i" % (self.ip, port)
225 path = "%s-%i" % (self.ip, port)
226 s.bind("ipc://%s" % path)
226 s.bind("ipc://%s" % path)
227 return port
227 return port
228
228
229 def load_connection_file(self):
229 def load_connection_file(self):
230 """load ip/port/hmac config from JSON connection file"""
230 """load ip/port/hmac config from JSON connection file"""
231 try:
231 try:
232 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
232 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
233 except IOError:
233 except IOError:
234 self.log.debug("Connection file not found: %s", self.connection_file)
234 self.log.debug("Connection file not found: %s", self.connection_file)
235 # This means I own it, so I will clean it up:
235 # This means I own it, so I will clean it up:
236 atexit.register(self.cleanup_connection_file)
236 atexit.register(self.cleanup_connection_file)
237 return
237 return
238 self.log.debug(u"Loading connection file %s", fname)
238 self.log.debug(u"Loading connection file %s", fname)
239 with open(fname) as f:
239 with open(fname) as f:
240 s = f.read()
240 s = f.read()
241 cfg = json.loads(s)
241 cfg = json.loads(s)
242 self.transport = cfg.get('transport', self.transport)
242 self.transport = cfg.get('transport', self.transport)
243 if self.ip == self._ip_default() and 'ip' in cfg:
243 if self.ip == self._ip_default() and 'ip' in cfg:
244 # not overridden by config or cl_args
244 # not overridden by config or cl_args
245 self.ip = cfg['ip']
245 self.ip = cfg['ip']
246 for channel in ('hb', 'shell', 'iopub', 'stdin', 'control'):
246 for channel in ('hb', 'shell', 'iopub', 'stdin', 'control'):
247 name = channel + '_port'
247 name = channel + '_port'
248 if getattr(self, name) == 0 and name in cfg:
248 if getattr(self, name) == 0 and name in cfg:
249 # not overridden by config or cl_args
249 # not overridden by config or cl_args
250 setattr(self, name, cfg[name])
250 setattr(self, name, cfg[name])
251 if 'key' in cfg:
251 if 'key' in cfg:
252 self.config.Session.key = str_to_bytes(cfg['key'])
252 self.config.Session.key = str_to_bytes(cfg['key'])
253
253
254 def write_connection_file(self):
254 def write_connection_file(self):
255 """write connection info to JSON file"""
255 """write connection info to JSON file"""
256 cf = self.abs_connection_file
256 cf = self.abs_connection_file
257 self.log.debug("Writing connection file: %s", cf)
257 self.log.debug("Writing connection file: %s", cf)
258 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,
259 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,
260 iopub_port=self.iopub_port, control_port=self.control_port)
260 iopub_port=self.iopub_port, control_port=self.control_port)
261
261
262 def cleanup_connection_file(self):
262 def cleanup_connection_file(self):
263 cf = self.abs_connection_file
263 cf = self.abs_connection_file
264 self.log.debug("Cleaning up connection file: %s", cf)
264 self.log.debug("Cleaning up connection file: %s", cf)
265 try:
265 try:
266 os.remove(cf)
266 os.remove(cf)
267 except (IOError, OSError):
267 except (IOError, OSError):
268 pass
268 pass
269
269
270 self.cleanup_ipc_files()
270 self.cleanup_ipc_files()
271
271
272 def cleanup_ipc_files(self):
272 def cleanup_ipc_files(self):
273 """cleanup ipc files if we wrote them"""
273 """cleanup ipc files if we wrote them"""
274 if self.transport != 'ipc':
274 if self.transport != 'ipc':
275 return
275 return
276 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):
277 ipcfile = "%s-%i" % (self.ip, port)
277 ipcfile = "%s-%i" % (self.ip, port)
278 try:
278 try:
279 os.remove(ipcfile)
279 os.remove(ipcfile)
280 except (IOError, OSError):
280 except (IOError, OSError):
281 pass
281 pass
282
282
283 def init_connection_file(self):
283 def init_connection_file(self):
284 if not self.connection_file:
284 if not self.connection_file:
285 self.connection_file = "kernel-%s.json"%os.getpid()
285 self.connection_file = "kernel-%s.json"%os.getpid()
286 try:
286 try:
287 self.load_connection_file()
287 self.load_connection_file()
288 except Exception:
288 except Exception:
289 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)
290 self.exit(1)
290 self.exit(1)
291
291
292 def init_sockets(self):
292 def init_sockets(self):
293 # Create a context, a session, and the kernel sockets.
293 # Create a context, a session, and the kernel sockets.
294 self.log.info("Starting the kernel at pid: %i", os.getpid())
294 self.log.info("Starting the kernel at pid: %i", os.getpid())
295 context = zmq.Context.instance()
295 context = zmq.Context.instance()
296 # Uncomment this to try closing the context.
296 # Uncomment this to try closing the context.
297 # atexit.register(context.term)
297 # atexit.register(context.term)
298
298
299 self.shell_socket = context.socket(zmq.ROUTER)
299 self.shell_socket = context.socket(zmq.ROUTER)
300 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)
301 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)
302
302
303 self.iopub_socket = context.socket(zmq.PUB)
303 self.iopub_socket = context.socket(zmq.PUB)
304 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)
305 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)
306
306
307 self.stdin_socket = context.socket(zmq.ROUTER)
307 self.stdin_socket = context.socket(zmq.ROUTER)
308 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)
309 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)
310
310
311 self.control_socket = context.socket(zmq.ROUTER)
311 self.control_socket = context.socket(zmq.ROUTER)
312 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)
313 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)
314
314
315 def init_heartbeat(self):
315 def init_heartbeat(self):
316 """start the heart beating"""
316 """start the heart beating"""
317 # heartbeat doesn't share context, because it mustn't be blocked
317 # heartbeat doesn't share context, because it mustn't be blocked
318 # 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
319 hb_ctx = zmq.Context()
319 hb_ctx = zmq.Context()
320 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))
321 self.hb_port = self.heartbeat.port
321 self.hb_port = self.heartbeat.port
322 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)
323 self.heartbeat.start()
323 self.heartbeat.start()
324
324
325 def log_connection_info(self):
325 def log_connection_info(self):
326 """display connection info, and store ports"""
326 """display connection info, and store ports"""
327 basename = os.path.basename(self.connection_file)
327 basename = os.path.basename(self.connection_file)
328 if basename == self.connection_file or \
328 if basename == self.connection_file or \
329 os.path.dirname(self.connection_file) == self.profile_dir.security_dir:
329 os.path.dirname(self.connection_file) == self.profile_dir.security_dir:
330 # use shortname
330 # use shortname
331 tail = basename
331 tail = basename
332 if self.profile != 'default':
332 if self.profile != 'default':
333 tail += " --profile %s" % self.profile
333 tail += " --profile %s" % self.profile
334 else:
334 else:
335 tail = self.connection_file
335 tail = self.connection_file
336 lines = [
336 lines = [
337 "To connect another client to this kernel, use:",
337 "To connect another client to this kernel, use:",
338 " --existing %s" % tail,
338 " --existing %s" % tail,
339 ]
339 ]
340 # log connection info
340 # log connection info
341 # info-level, so often not shown.
341 # info-level, so often not shown.
342 # frontends should use the %connect_info magic
342 # frontends should use the %connect_info magic
343 # to see the connection info
343 # to see the connection info
344 for line in lines:
344 for line in lines:
345 self.log.info(line)
345 self.log.info(line)
346 # 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`)
347 if not self.parent_handle:
347 if not self.parent_handle:
348 io.rprint(_ctrl_c_message)
348 io.rprint(_ctrl_c_message)
349 for line in lines:
349 for line in lines:
350 io.rprint(line)
350 io.rprint(line)
351
351
352 self.ports = dict(shell=self.shell_port, iopub=self.iopub_port,
352 self.ports = dict(shell=self.shell_port, iopub=self.iopub_port,
353 stdin=self.stdin_port, hb=self.hb_port,
353 stdin=self.stdin_port, hb=self.hb_port,
354 control=self.control_port)
354 control=self.control_port)
355
355
356 def init_session(self):
356 def init_session(self):
357 """create our session object"""
357 """create our session object"""
358 default_secure(self.config)
358 default_secure(self.config)
359 self.session = Session(parent=self, username=u'kernel')
359 self.session = Session(parent=self, username=u'kernel')
360
360
361 def init_blackhole(self):
361 def init_blackhole(self):
362 """redirects stdout/stderr to devnull if necessary"""
362 """redirects stdout/stderr to devnull if necessary"""
363 if self.no_stdout or self.no_stderr:
363 if self.no_stdout or self.no_stderr:
364 blackhole = open(os.devnull, 'w')
364 blackhole = open(os.devnull, 'w')
365 if self.no_stdout:
365 if self.no_stdout:
366 sys.stdout = sys.__stdout__ = blackhole
366 sys.stdout = sys.__stdout__ = blackhole
367 if self.no_stderr:
367 if self.no_stderr:
368 sys.stderr = sys.__stderr__ = blackhole
368 sys.stderr = sys.__stderr__ = blackhole
369
369
370 def init_io(self):
370 def init_io(self):
371 """Redirect input streams and set a display hook."""
371 """Redirect input streams and set a display hook."""
372 if self.outstream_class:
372 if self.outstream_class:
373 outstream_factory = import_item(str(self.outstream_class))
373 outstream_factory = import_item(str(self.outstream_class))
374 sys.stdout = outstream_factory(self.session, self.iopub_socket, u'stdout')
374 sys.stdout = outstream_factory(self.session, self.iopub_socket, u'stdout')
375 sys.stderr = outstream_factory(self.session, self.iopub_socket, u'stderr')
375 sys.stderr = outstream_factory(self.session, self.iopub_socket, u'stderr')
376 if self.displayhook_class:
376 if self.displayhook_class:
377 displayhook_factory = import_item(str(self.displayhook_class))
377 displayhook_factory = import_item(str(self.displayhook_class))
378 sys.displayhook = displayhook_factory(self.session, self.iopub_socket)
378 sys.displayhook = displayhook_factory(self.session, self.iopub_socket)
379
379
380 def init_signal(self):
380 def init_signal(self):
381 signal.signal(signal.SIGINT, signal.SIG_IGN)
381 signal.signal(signal.SIGINT, signal.SIG_IGN)
382
382
383 def init_kernel(self):
383 def init_kernel(self):
384 """Create the Kernel object itself"""
384 """Create the Kernel object itself"""
385 shell_stream = ZMQStream(self.shell_socket)
385 shell_stream = ZMQStream(self.shell_socket)
386 control_stream = ZMQStream(self.control_socket)
386 control_stream = ZMQStream(self.control_socket)
387
387
388 kernel_factory = import_item(str(self.kernel_class))
388 kernel_factory = import_item(str(self.kernel_class))
389
389
390 kernel = kernel_factory(parent=self, session=self.session,
390 kernel = kernel_factory(parent=self, session=self.session,
391 shell_streams=[shell_stream, control_stream],
391 shell_streams=[shell_stream, control_stream],
392 iopub_socket=self.iopub_socket,
392 iopub_socket=self.iopub_socket,
393 stdin_socket=self.stdin_socket,
393 stdin_socket=self.stdin_socket,
394 log=self.log,
394 log=self.log,
395 profile_dir=self.profile_dir,
395 profile_dir=self.profile_dir,
396 )
396 )
397 kernel.record_ports(self.ports)
397 kernel.record_ports(self.ports)
398 self.kernel = kernel
398 self.kernel = kernel
399
399
400 def init_gui_pylab(self):
400 def init_gui_pylab(self):
401 """Enable GUI event loop integration, taking pylab into account."""
401 """Enable GUI event loop integration, taking pylab into account."""
402
402
403 # Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab`
403 # Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab`
404 # to ensure that any exception is printed straight to stderr.
404 # to ensure that any exception is printed straight to stderr.
405 # Normally _showtraceback associates the reply with an execution,
405 # Normally _showtraceback associates the reply with an execution,
406 # which means frontends will never draw it, as this exception
406 # which means frontends will never draw it, as this exception
407 # is not associated with any execute request.
407 # is not associated with any execute request.
408
408
409 shell = self.shell
409 shell = self.shell
410 _showtraceback = shell._showtraceback
410 _showtraceback = shell._showtraceback
411 try:
411 try:
412 # replace pyerr-sending traceback with stderr
412 # replace pyerr-sending traceback with stderr
413 def print_tb(etype, evalue, stb):
413 def print_tb(etype, evalue, stb):
414 print ("GUI event loop or pylab initialization failed",
414 print ("GUI event loop or pylab initialization failed",
415 file=io.stderr)
415 file=io.stderr)
416 print (shell.InteractiveTB.stb2text(stb), file=io.stderr)
416 print (shell.InteractiveTB.stb2text(stb), file=io.stderr)
417 shell._showtraceback = print_tb
417 shell._showtraceback = print_tb
418 InteractiveShellApp.init_gui_pylab(self)
418 InteractiveShellApp.init_gui_pylab(self)
419 finally:
419 finally:
420 shell._showtraceback = _showtraceback
420 shell._showtraceback = _showtraceback
421
421
422 def init_shell(self):
422 def init_shell(self):
423 self.shell = self.kernel.shell
423 self.shell = self.kernel.shell
424 self.shell.configurables.append(self)
424 self.shell.configurables.append(self)
425
425
426 @catch_config_error
426 @catch_config_error
427 def initialize(self, argv=None):
427 def initialize(self, argv=None):
428 super(IPKernelApp, self).initialize(argv)
428 super(IPKernelApp, self).initialize(argv)
429 self.init_blackhole()
429 self.init_blackhole()
430 self.init_connection_file()
430 self.init_connection_file()
431 self.init_session()
431 self.init_session()
432 self.init_poller()
432 self.init_poller()
433 self.init_sockets()
433 self.init_sockets()
434 self.init_heartbeat()
434 self.init_heartbeat()
435 # writing/displaying connection info must be *after* init_sockets/heartbeat
435 # writing/displaying connection info must be *after* init_sockets/heartbeat
436 self.log_connection_info()
436 self.log_connection_info()
437 self.write_connection_file()
437 self.write_connection_file()
438 self.init_io()
438 self.init_io()
439 self.init_signal()
439 self.init_signal()
440 self.init_kernel()
440 self.init_kernel()
441 # shell init steps
441 # shell init steps
442 self.init_path()
442 self.init_path()
443 self.init_shell()
443 self.init_shell()
444 self.init_gui_pylab()
444 self.init_gui_pylab()
445 self.init_extensions()
445 self.init_extensions()
446 self.init_code()
446 self.init_code()
447 # flush stdout/stderr, so that anything written to these streams during
447 # flush stdout/stderr, so that anything written to these streams during
448 # initialization do not get associated with the first execution request
448 # initialization do not get associated with the first execution request
449 sys.stdout.flush()
449 sys.stdout.flush()
450 sys.stderr.flush()
450 sys.stderr.flush()
451
451
452 def start(self):
452 def start(self):
453 if self.poller is not None:
453 if self.poller is not None:
454 self.poller.start()
454 self.poller.start()
455 self.kernel.start()
455 self.kernel.start()
456 try:
456 try:
457 ioloop.IOLoop.instance().start()
457 ioloop.IOLoop.instance().start()
458 except KeyboardInterrupt:
458 except KeyboardInterrupt:
459 pass
459 pass
460
460
461 launch_new_instance = IPKernelApp.launch_instance
461
462
462 def main():
463 def main():
463 """Run an IPKernel as an application"""
464 """Run an IPKernel as an application"""
464 app = IPKernelApp.instance()
465 app = IPKernelApp.instance()
465 app.initialize()
466 app.initialize()
466 app.start()
467 app.start()
467
468
468
469
469 if __name__ == '__main__':
470 if __name__ == '__main__':
470 main()
471 main()
General Comments 0
You need to be logged in to leave comments. Login now