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. |
|
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( |
|
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 |
|
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. |
|
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 | |||
@@ -181,7 +183,9 b' class BackgroundJobManager(object):' | |||||
181 | job = BackgroundJobExpr(func_or_exp, glob, loc) |
|
183 | job = BackgroundJobExpr(func_or_exp, glob, loc) | |
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