Show More
@@ -21,6 +21,7 b' Authors:' | |||||
21 | #----------------------------------------------------------------------------- |
|
21 | #----------------------------------------------------------------------------- | |
22 |
|
22 | |||
23 | # stdlib imports |
|
23 | # stdlib imports | |
|
24 | import atexit | |||
24 | import json |
|
25 | import json | |
25 | import os |
|
26 | import os | |
26 | import signal |
|
27 | import signal | |
@@ -234,6 +235,9 b' class IPythonMixinConsoleApp(Configurable):' | |||||
234 | argument does not match an existing file, it will be interpreted as a |
|
235 | argument does not match an existing file, it will be interpreted as a | |
235 | fileglob, and the matching file in the current profile's security dir |
|
236 | fileglob, and the matching file in the current profile's security dir | |
236 | with the latest access time will be used. |
|
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 | if self.existing: |
|
242 | if self.existing: | |
239 | try: |
|
243 | try: | |
@@ -243,6 +247,20 b' class IPythonMixinConsoleApp(Configurable):' | |||||
243 | self.exit(1) |
|
247 | self.exit(1) | |
244 | self.log.info("Connecting to existing kernel: %s" % cf) |
|
248 | self.log.info("Connecting to existing kernel: %s" % cf) | |
245 | self.connection_file = cf |
|
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 | # should load_connection_file only be used for existing? |
|
264 | # should load_connection_file only be used for existing? | |
247 | # as it is now, this allows reusing ports if an existing |
|
265 | # as it is now, this allows reusing ports if an existing | |
248 | # file is requested |
|
266 | # file is requested | |
@@ -315,21 +333,21 b' class IPythonMixinConsoleApp(Configurable):' | |||||
315 | self.log.critical("--existing %s" % self.connection_file) |
|
333 | self.log.critical("--existing %s" % self.connection_file) | |
316 |
|
334 | |||
317 | def _new_connection_file(self): |
|
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 | def init_kernel_manager(self): |
|
348 | def init_kernel_manager(self): | |
321 | # Don't let Qt or ZMQ swallow KeyboardInterupts. |
|
349 | # Don't let Qt or ZMQ swallow KeyboardInterupts. | |
322 | signal.signal(signal.SIGINT, signal.SIG_DFL) |
|
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 | # Create a KernelManager and start a kernel. |
|
352 | # Create a KernelManager and start a kernel. | |
335 | self.kernel_manager = self.kernel_manager_class( |
|
353 | self.kernel_manager = self.kernel_manager_class( | |
@@ -338,7 +356,7 b' class IPythonMixinConsoleApp(Configurable):' | |||||
338 | iopub_port=self.iopub_port, |
|
356 | iopub_port=self.iopub_port, | |
339 | stdin_port=self.stdin_port, |
|
357 | stdin_port=self.stdin_port, | |
340 | hb_port=self.hb_port, |
|
358 | hb_port=self.hb_port, | |
341 | connection_file=cf, |
|
359 | connection_file=self.connection_file, | |
342 | config=self.config, |
|
360 | config=self.config, | |
343 | ) |
|
361 | ) | |
344 | # start the kernel |
|
362 | # start the kernel | |
@@ -349,6 +367,7 b' class IPythonMixinConsoleApp(Configurable):' | |||||
349 | elif self.sshserver: |
|
367 | elif self.sshserver: | |
350 | # ssh, write new connection file |
|
368 | # ssh, write new connection file | |
351 | self.kernel_manager.write_connection_file() |
|
369 | self.kernel_manager.write_connection_file() | |
|
370 | atexit.register(self.kernel_manager.cleanup_connection_file) | |||
352 | self.kernel_manager.start_channels() |
|
371 | self.kernel_manager.start_channels() | |
353 |
|
372 | |||
354 |
|
373 |
@@ -27,7 +27,7 b' import sys' | |||||
27 | import uuid |
|
27 | import uuid | |
28 |
|
28 | |||
29 | # System library imports |
|
29 | # System library imports | |
30 | from IPython.external.qt import QtGui |
|
30 | from IPython.external.qt import QtCore, QtGui | |
31 |
|
31 | |||
32 | # Local imports |
|
32 | # Local imports | |
33 | from IPython.config.application import boolean_flag, catch_config_error |
|
33 | from IPython.config.application import boolean_flag, catch_config_error | |
@@ -298,12 +298,26 b' class IPythonQtConsoleApp(BaseIPythonApplication, IPythonMixinConsoleApp):' | |||||
298 | else: |
|
298 | else: | |
299 | raise IOError("Stylesheet %r not found."%self.stylesheet) |
|
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 | @catch_config_error |
|
314 | @catch_config_error | |
302 | def initialize(self, argv=None): |
|
315 | def initialize(self, argv=None): | |
303 | super(IPythonQtConsoleApp, self).initialize(argv) |
|
316 | super(IPythonQtConsoleApp, self).initialize(argv) | |
304 | IPythonMixinConsoleApp.initialize(self,argv) |
|
317 | IPythonMixinConsoleApp.initialize(self,argv) | |
305 | self.init_qt_elements() |
|
318 | self.init_qt_elements() | |
306 | self.init_colors() |
|
319 | self.init_colors() | |
|
320 | self.init_signal() | |||
307 |
|
321 | |||
308 | def start(self): |
|
322 | def start(self): | |
309 |
|
323 |
@@ -639,14 +639,8 b' class KernelManager(HasTraits):' | |||||
639 | self.session = Session(config=self.config) |
|
639 | self.session = Session(config=self.config) | |
640 |
|
640 | |||
641 | def __del__(self): |
|
641 | def __del__(self): | |
642 |
|
|
642 | self.cleanup_connection_file() | |
643 | # cleanup connection files on full shutdown of kernel we started |
|
643 | ||
644 | self._connection_file_written = False |
|
|||
645 | try: |
|
|||
646 | os.remove(self.connection_file) |
|
|||
647 | except IOError: |
|
|||
648 | pass |
|
|||
649 |
|
||||
650 |
|
644 | |||
651 | #-------------------------------------------------------------------------- |
|
645 | #-------------------------------------------------------------------------- | |
652 | # Channel management methods: |
|
646 | # Channel management methods: | |
@@ -694,6 +688,19 b' class KernelManager(HasTraits):' | |||||
694 | # Kernel process management methods: |
|
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 | def load_connection_file(self): |
|
704 | def load_connection_file(self): | |
698 | """load connection info from JSON dict in self.connection_file""" |
|
705 | """load connection info from JSON dict in self.connection_file""" | |
699 | with open(self.connection_file) as f: |
|
706 | with open(self.connection_file) as f: |
General Comments 0
You need to be logged in to leave comments.
Login now