##// 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 import signal
18 import signal
19 import time
19 import time
20 from subprocess import Popen, PIPE
20 from subprocess import Popen, PIPE
21 import atexit
21
22
22 # Our own packages
23 # Our own packages
23 from IPython.config.configurable import Configurable
24 from IPython.config.configurable import Configurable
@@ -134,6 +135,11 b' class ScriptMagics(Magics, Configurable):'
134 self._generate_script_magics()
135 self._generate_script_magics()
135 Magics.__init__(self, shell=shell)
136 Magics.__init__(self, shell=shell)
136 self.job_manager = BackgroundJobManager()
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 def _generate_script_magics(self):
144 def _generate_script_magics(self):
139 cell_magics = self.magics['cell']
145 cell_magics = self.magics['cell']
@@ -196,11 +202,13 b' class ScriptMagics(Magics, Configurable):'
196
202
197 cell = cell.encode('utf8', 'replace')
203 cell = cell.encode('utf8', 'replace')
198 if args.bg:
204 if args.bg:
205 self.bg_processes.append(p)
206 self._gc_bg_processes()
199 if args.out:
207 if args.out:
200 self.shell.user_ns[args.out] = p.stdout
208 self.shell.user_ns[args.out] = p.stdout
201 if args.err:
209 if args.err:
202 self.shell.user_ns[args.err] = p.stderr
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 if args.proc:
212 if args.proc:
205 self.shell.user_ns[args.proc] = p
213 self.shell.user_ns[args.proc] = p
206 return
214 return
@@ -245,3 +253,36 b' class ScriptMagics(Magics, Configurable):'
245 p.stdin.write(cell)
253 p.stdin.write(cell)
246 p.stdin.close()
254 p.stdin.close()
247 p.wait()
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 @web.authenticated
345 @web.authenticated
346 def delete(self, kernel_id):
346 def delete(self, kernel_id):
347 km = self.application.kernel_manager
347 km = self.application.kernel_manager
348 km.kill_kernel(kernel_id)
348 km.shutdown_kernel(kernel_id)
349 self.set_status(204)
349 self.set_status(204)
350 self.finish()
350 self.finish()
351
351
@@ -43,7 +43,7 b' class MultiKernelManager(LoggingConfigurable):'
43 """A class for managing multiple kernels."""
43 """A class for managing multiple kernels."""
44
44
45 kernel_manager_class = DottedObjectName(
45 kernel_manager_class = DottedObjectName(
46 "IPython.zmq.kernelmanager.KernelManager", config=True,
46 "IPython.zmq.blockingkernelmanager.BlockingKernelManager", config=True,
47 help="""The kernel manager class. This is configurable to allow
47 help="""The kernel manager class. This is configurable to allow
48 subclassing of the KernelManager for customized behavior.
48 subclassing of the KernelManager for customized behavior.
49 """
49 """
@@ -87,9 +87,22 b' class MultiKernelManager(LoggingConfigurable):'
87 config=self.config,
87 config=self.config,
88 )
88 )
89 km.start_kernel(**kwargs)
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 self._kernels[kernel_id] = km
92 self._kernels[kernel_id] = km
91 return kernel_id
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 def kill_kernel(self, kernel_id):
106 def kill_kernel(self, kernel_id):
94 """Kill a kernel by its kernel uuid.
107 """Kill a kernel by its kernel uuid.
95
108
@@ -266,6 +279,13 b' class MappingKernelManager(MultiKernelManager):'
266 self.log.info("Using existing kernel: %s" % kernel_id)
279 self.log.info("Using existing kernel: %s" % kernel_id)
267 return kernel_id
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 def kill_kernel(self, kernel_id):
289 def kill_kernel(self, kernel_id):
270 """Kill a kernel and remove its notebook association."""
290 """Kill a kernel and remove its notebook association."""
271 self._check_kernel_id(kernel_id)
291 self._check_kernel_id(kernel_id)
@@ -283,7 +303,7 b' class MappingKernelManager(MultiKernelManager):'
283 """Restart a kernel while keeping clients connected."""
303 """Restart a kernel while keeping clients connected."""
284 self._check_kernel_id(kernel_id)
304 self._check_kernel_id(kernel_id)
285 km = self.get_kernel(kernel_id)
305 km = self.get_kernel(kernel_id)
286 km.restart_kernel(now=True)
306 km.restart_kernel()
287 self.log.info("Kernel restarted: %s" % kernel_id)
307 self.log.info("Kernel restarted: %s" % kernel_id)
288 return kernel_id
308 return kernel_id
289
309
@@ -537,9 +537,9 b' class NotebookApp(BaseIPythonApplication):'
537 """
537 """
538 self.log.info('Shutting down kernels')
538 self.log.info('Shutting down kernels')
539 km = self.kernel_manager
539 km = self.kernel_manager
540 # copy list, since kill_kernel deletes keys
540 # copy list, since shutdown_kernel deletes keys
541 for kid in list(km.kernel_ids):
541 for kid in list(km.kernel_ids):
542 km.kill_kernel(kid)
542 km.shutdown_kernel(kid)
543
543
544 def start(self):
544 def start(self):
545 ip = self.ip if self.ip else '[all ip addresses on your system]'
545 ip = self.ip if self.ip else '[all ip addresses on your system]'
@@ -140,6 +140,8 b' class BackgroundJobManager(object):'
140 In both cases, the result is stored in the job.result field of the
140 In both cases, the result is stored in the job.result field of the
141 background job object.
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 Notes and caveats:
146 Notes and caveats:
145
147
@@ -182,6 +184,8 b' class BackgroundJobManager(object):'
182 else:
184 else:
183 raise TypeError('invalid args for new job')
185 raise TypeError('invalid args for new job')
184
186
187 if kwargs.get('daemon', False):
188 job.daemon = True
185 job.num = len(self.all)+1 if self.all else 0
189 job.num = len(self.all)+1 if self.all else 0
186 self.running.append(job)
190 self.running.append(job)
187 self.all[job.num] = job
191 self.all[job.num] = job
General Comments 0
You need to be logged in to leave comments. Login now