##// END OF EJS Templates
improve cleanup of connection files...
MinRK -
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,13 +639,7 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 if self._connection_file_written:
642 self.cleanup_connection_file()
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
643
650
644
651 #--------------------------------------------------------------------------
645 #--------------------------------------------------------------------------
@@ -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