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. |
|
|
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( |
|
|
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 |
|
|
540 | # copy list, since shutdown_kernel deletes keys | |
|
541 | 541 | for kid in list(km.kernel_ids): |
|
542 |
km. |
|
|
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