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