Show More
@@ -21,6 +21,7 b' Authors:' | |||
|
21 | 21 | #----------------------------------------------------------------------------- |
|
22 | 22 | |
|
23 | 23 | # stdlib imports |
|
24 | import atexit | |
|
24 | 25 | import json |
|
25 | 26 | import os |
|
26 | 27 | import signal |
@@ -234,6 +235,9 b' class IPythonMixinConsoleApp(Configurable):' | |||
|
234 | 235 | argument does not match an existing file, it will be interpreted as a |
|
235 | 236 | fileglob, and the matching file in the current profile's security dir |
|
236 | 237 | with the latest access time will be used. |
|
238 | ||
|
239 | After this method is called, self.connection_file contains the *full path* | |
|
240 | to the connection file, never just its name. | |
|
237 | 241 | """ |
|
238 | 242 | if self.existing: |
|
239 | 243 | try: |
@@ -243,6 +247,20 b' class IPythonMixinConsoleApp(Configurable):' | |||
|
243 | 247 | self.exit(1) |
|
244 | 248 | self.log.info("Connecting to existing kernel: %s" % cf) |
|
245 | 249 | self.connection_file = cf |
|
250 | else: | |
|
251 | # not existing, check if we are going to write the file | |
|
252 | # and ensure that self.connection_file is a full path, not just the shortname | |
|
253 | try: | |
|
254 | cf = find_connection_file(self.connection_file) | |
|
255 | except Exception: | |
|
256 | # file might not exist | |
|
257 | if self.connection_file == os.path.basename(self.connection_file): | |
|
258 | # just shortname, put it in security dir | |
|
259 | cf = os.path.join(self.profile_dir.security_dir, self.connection_file) | |
|
260 | else: | |
|
261 | cf = self.connection_file | |
|
262 | self.connection_file = cf | |
|
263 | ||
|
246 | 264 | # should load_connection_file only be used for existing? |
|
247 | 265 | # as it is now, this allows reusing ports if an existing |
|
248 | 266 | # file is requested |
@@ -315,21 +333,21 b' class IPythonMixinConsoleApp(Configurable):' | |||
|
315 | 333 | self.log.critical("--existing %s" % self.connection_file) |
|
316 | 334 | |
|
317 | 335 | def _new_connection_file(self): |
|
318 | return os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % uuid.uuid4()) | |
|
336 | cf = '' | |
|
337 | while not cf: | |
|
338 | # we don't need a 128b id to distinguish kernels, use more readable | |
|
339 | # 48b node segment (12 hex chars). Users running more than 32k simultaneous | |
|
340 | # kernels can subclass. | |
|
341 | ident = str(uuid.uuid4()).split('-')[-1] | |
|
342 | cf = os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % ident) | |
|
343 | # only keep if it's actually new. Protect against unlikely collision | |
|
344 | # in 48b random search space | |
|
345 | cf = cf if not os.path.exists(cf) else '' | |
|
346 | return cf | |
|
319 | 347 | |
|
320 | 348 | def init_kernel_manager(self): |
|
321 | 349 | # Don't let Qt or ZMQ swallow KeyboardInterupts. |
|
322 | 350 | signal.signal(signal.SIGINT, signal.SIG_DFL) |
|
323 | sec = self.profile_dir.security_dir | |
|
324 | try: | |
|
325 | cf = filefind(self.connection_file, ['.', sec]) | |
|
326 | except IOError: | |
|
327 | # file might not exist | |
|
328 | if self.connection_file == os.path.basename(self.connection_file): | |
|
329 | # just shortname, put it in security dir | |
|
330 | cf = os.path.join(sec, self.connection_file) | |
|
331 | else: | |
|
332 | cf = self.connection_file | |
|
333 | 351 | |
|
334 | 352 | # Create a KernelManager and start a kernel. |
|
335 | 353 | self.kernel_manager = self.kernel_manager_class( |
@@ -338,7 +356,7 b' class IPythonMixinConsoleApp(Configurable):' | |||
|
338 | 356 | iopub_port=self.iopub_port, |
|
339 | 357 | stdin_port=self.stdin_port, |
|
340 | 358 | hb_port=self.hb_port, |
|
341 | connection_file=cf, | |
|
359 | connection_file=self.connection_file, | |
|
342 | 360 | config=self.config, |
|
343 | 361 | ) |
|
344 | 362 | # start the kernel |
@@ -349,6 +367,7 b' class IPythonMixinConsoleApp(Configurable):' | |||
|
349 | 367 | elif self.sshserver: |
|
350 | 368 | # ssh, write new connection file |
|
351 | 369 | self.kernel_manager.write_connection_file() |
|
370 | atexit.register(self.kernel_manager.cleanup_connection_file) | |
|
352 | 371 | self.kernel_manager.start_channels() |
|
353 | 372 | |
|
354 | 373 |
@@ -27,7 +27,7 b' import sys' | |||
|
27 | 27 | import uuid |
|
28 | 28 | |
|
29 | 29 | # System library imports |
|
30 | from IPython.external.qt import QtGui | |
|
30 | from IPython.external.qt import QtCore, QtGui | |
|
31 | 31 | |
|
32 | 32 | # Local imports |
|
33 | 33 | from IPython.config.application import boolean_flag, catch_config_error |
@@ -298,12 +298,26 b' class IPythonQtConsoleApp(BaseIPythonApplication, IPythonMixinConsoleApp):' | |||
|
298 | 298 | else: |
|
299 | 299 | raise IOError("Stylesheet %r not found."%self.stylesheet) |
|
300 | 300 | |
|
301 | def init_signal(self): | |
|
302 | """allow clean shutdown on sigint""" | |
|
303 | signal.signal(signal.SIGINT, lambda sig, frame: self.exit(-2)) | |
|
304 | # need a timer, so that QApplication doesn't block until a real | |
|
305 | # Qt event fires (can require mouse movement) | |
|
306 | # timer trick from http://stackoverflow.com/q/4938723/938949 | |
|
307 | timer = QtCore.QTimer() | |
|
308 | # Let the interpreter run each 200 ms: | |
|
309 | timer.timeout.connect(lambda: None) | |
|
310 | timer.start(200) | |
|
311 | # hold onto ref, so the timer doesn't get cleaned up | |
|
312 | self._sigint_timer = timer | |
|
313 | ||
|
301 | 314 | @catch_config_error |
|
302 | 315 | def initialize(self, argv=None): |
|
303 | 316 | super(IPythonQtConsoleApp, self).initialize(argv) |
|
304 | 317 | IPythonMixinConsoleApp.initialize(self,argv) |
|
305 | 318 | self.init_qt_elements() |
|
306 | 319 | self.init_colors() |
|
320 | self.init_signal() | |
|
307 | 321 | |
|
308 | 322 | def start(self): |
|
309 | 323 |
@@ -639,14 +639,8 b' class KernelManager(HasTraits):' | |||
|
639 | 639 | self.session = Session(config=self.config) |
|
640 | 640 | |
|
641 | 641 | def __del__(self): |
|
642 |
|
|
|
643 | # cleanup connection files on full shutdown of kernel we started | |
|
644 | self._connection_file_written = False | |
|
645 | try: | |
|
646 | os.remove(self.connection_file) | |
|
647 | except IOError: | |
|
648 | pass | |
|
649 | ||
|
642 | self.cleanup_connection_file() | |
|
643 | ||
|
650 | 644 | |
|
651 | 645 | #-------------------------------------------------------------------------- |
|
652 | 646 | # Channel management methods: |
@@ -694,6 +688,19 b' class KernelManager(HasTraits):' | |||
|
694 | 688 | # Kernel process management methods: |
|
695 | 689 | #-------------------------------------------------------------------------- |
|
696 | 690 | |
|
691 | def cleanup_connection_file(self): | |
|
692 | """cleanup connection file *if we wrote it* | |
|
693 | ||
|
694 | Will not raise if the connection file was already removed somehow. | |
|
695 | """ | |
|
696 | if self._connection_file_written: | |
|
697 | # cleanup connection files on full shutdown of kernel we started | |
|
698 | self._connection_file_written = False | |
|
699 | try: | |
|
700 | os.remove(self.connection_file) | |
|
701 | except OSError: | |
|
702 | pass | |
|
703 | ||
|
697 | 704 | def load_connection_file(self): |
|
698 | 705 | """load connection info from JSON dict in self.connection_file""" |
|
699 | 706 | with open(self.connection_file) as f: |
General Comments 0
You need to be logged in to leave comments.
Login now