##// END OF EJS Templates
Merge pull request #1981 from tkf/kill-bg-processes...
Min RK -
r7645:a1c5e371 merge
parent child Browse files
Show More
@@ -18,6 +18,7 b' import sys'
18 18 import signal
19 19 import time
20 20 from subprocess import Popen, PIPE
21 import atexit
21 22
22 23 # Our own packages
23 24 from IPython.config.configurable import Configurable
@@ -134,6 +135,11 b' class ScriptMagics(Magics, Configurable):'
134 135 self._generate_script_magics()
135 136 Magics.__init__(self, shell=shell)
136 137 self.job_manager = BackgroundJobManager()
138 self.bg_processes = []
139 atexit.register(self.kill_bg_processes)
140
141 def __del__(self):
142 self.kill_bg_processes()
137 143
138 144 def _generate_script_magics(self):
139 145 cell_magics = self.magics['cell']
@@ -196,11 +202,13 b' class ScriptMagics(Magics, Configurable):'
196 202
197 203 cell = cell.encode('utf8', 'replace')
198 204 if args.bg:
205 self.bg_processes.append(p)
206 self._gc_bg_processes()
199 207 if args.out:
200 208 self.shell.user_ns[args.out] = p.stdout
201 209 if args.err:
202 210 self.shell.user_ns[args.err] = p.stderr
203 self.job_manager.new(self._run_script, p, cell)
211 self.job_manager.new(self._run_script, p, cell, daemon=True)
204 212 if args.proc:
205 213 self.shell.user_ns[args.proc] = p
206 214 return
@@ -245,3 +253,36 b' class ScriptMagics(Magics, Configurable):'
245 253 p.stdin.write(cell)
246 254 p.stdin.close()
247 255 p.wait()
256
257 @line_magic("killbgscripts")
258 def killbgscripts(self, _nouse_=''):
259 """Kill all BG processes started by %%script and its family."""
260 self.kill_bg_processes()
261 print "All background processes were killed."
262
263 def kill_bg_processes(self):
264 """Kill all BG processes which are still running."""
265 for p in self.bg_processes:
266 if p.poll() is None:
267 try:
268 p.send_signal(signal.SIGINT)
269 except:
270 pass
271 time.sleep(0.1)
272 for p in self.bg_processes:
273 if p.poll() is None:
274 try:
275 p.terminate()
276 except:
277 pass
278 time.sleep(0.1)
279 for p in self.bg_processes:
280 if p.poll() is None:
281 try:
282 p.kill()
283 except:
284 pass
285 self._gc_bg_processes()
286
287 def _gc_bg_processes(self):
288 self.bg_processes = [p for p in self.bg_processes if p.poll() is None]
@@ -345,7 +345,7 b' class KernelHandler(AuthenticatedHandler):'
345 345 @web.authenticated
346 346 def delete(self, kernel_id):
347 347 km = self.application.kernel_manager
348 km.kill_kernel(kernel_id)
348 km.shutdown_kernel(kernel_id)
349 349 self.set_status(204)
350 350 self.finish()
351 351
@@ -43,7 +43,7 b' class MultiKernelManager(LoggingConfigurable):'
43 43 """A class for managing multiple kernels."""
44 44
45 45 kernel_manager_class = DottedObjectName(
46 "IPython.zmq.kernelmanager.KernelManager", config=True,
46 "IPython.zmq.blockingkernelmanager.BlockingKernelManager", config=True,
47 47 help="""The kernel manager class. This is configurable to allow
48 48 subclassing of the KernelManager for customized behavior.
49 49 """
@@ -87,9 +87,22 b' class MultiKernelManager(LoggingConfigurable):'
87 87 config=self.config,
88 88 )
89 89 km.start_kernel(**kwargs)
90 # start just the shell channel, needed for graceful restart
91 km.start_channels(shell=True, sub=False, stdin=False, hb=False)
90 92 self._kernels[kernel_id] = km
91 93 return kernel_id
92 94
95 def shutdown_kernel(self, kernel_id):
96 """Shutdown a kernel by its kernel uuid.
97
98 Parameters
99 ==========
100 kernel_id : uuid
101 The id of the kernel to shutdown.
102 """
103 self.get_kernel(kernel_id).shutdown_kernel()
104 del self._kernels[kernel_id]
105
93 106 def kill_kernel(self, kernel_id):
94 107 """Kill a kernel by its kernel uuid.
95 108
@@ -266,6 +279,13 b' class MappingKernelManager(MultiKernelManager):'
266 279 self.log.info("Using existing kernel: %s" % kernel_id)
267 280 return kernel_id
268 281
282 def shutdown_kernel(self, kernel_id):
283 """Shutdown a kernel and remove its notebook association."""
284 self._check_kernel_id(kernel_id)
285 super(MappingKernelManager, self).shutdown_kernel(kernel_id)
286 self.delete_mapping_for_kernel(kernel_id)
287 self.log.info("Kernel shutdown: %s" % kernel_id)
288
269 289 def kill_kernel(self, kernel_id):
270 290 """Kill a kernel and remove its notebook association."""
271 291 self._check_kernel_id(kernel_id)
@@ -283,7 +303,7 b' class MappingKernelManager(MultiKernelManager):'
283 303 """Restart a kernel while keeping clients connected."""
284 304 self._check_kernel_id(kernel_id)
285 305 km = self.get_kernel(kernel_id)
286 km.restart_kernel(now=True)
306 km.restart_kernel()
287 307 self.log.info("Kernel restarted: %s" % kernel_id)
288 308 return kernel_id
289 309
@@ -537,9 +537,9 b' class NotebookApp(BaseIPythonApplication):'
537 537 """
538 538 self.log.info('Shutting down kernels')
539 539 km = self.kernel_manager
540 # copy list, since kill_kernel deletes keys
540 # copy list, since shutdown_kernel deletes keys
541 541 for kid in list(km.kernel_ids):
542 km.kill_kernel(kid)
542 km.shutdown_kernel(kid)
543 543
544 544 def start(self):
545 545 ip = self.ip if self.ip else '[all ip addresses on your system]'
@@ -140,6 +140,8 b' class BackgroundJobManager(object):'
140 140 In both cases, the result is stored in the job.result field of the
141 141 background job object.
142 142
143 You can set `daemon` attribute of the thread by giving the keyword
144 argument `daemon`.
143 145
144 146 Notes and caveats:
145 147
@@ -181,7 +183,9 b' class BackgroundJobManager(object):'
181 183 job = BackgroundJobExpr(func_or_exp, glob, loc)
182 184 else:
183 185 raise TypeError('invalid args for new job')
184
186
187 if kwargs.get('daemon', False):
188 job.daemon = True
185 189 job.num = len(self.all)+1 if self.all else 0
186 190 self.running.append(job)
187 191 self.all[job.num] = job
General Comments 0
You need to be logged in to leave comments. Login now