##// END OF EJS Templates
Revert "Add Julia to default script magics."
Matthias Bussonnier -
Show More
@@ -1,280 +1,279 b''
1 """Magic functions for running cells in various scripts."""
1 """Magic functions for running cells in various scripts."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import errno
6 import errno
7 import os
7 import os
8 import sys
8 import sys
9 import signal
9 import signal
10 import time
10 import time
11 from subprocess import Popen, PIPE
11 from subprocess import Popen, PIPE
12 import atexit
12 import atexit
13
13
14 from IPython.core import magic_arguments
14 from IPython.core import magic_arguments
15 from IPython.core.magic import (
15 from IPython.core.magic import (
16 Magics, magics_class, line_magic, cell_magic
16 Magics, magics_class, line_magic, cell_magic
17 )
17 )
18 from IPython.lib.backgroundjobs import BackgroundJobManager
18 from IPython.lib.backgroundjobs import BackgroundJobManager
19 from IPython.utils import py3compat
19 from IPython.utils import py3compat
20 from IPython.utils.process import arg_split
20 from IPython.utils.process import arg_split
21 from traitlets import List, Dict, default
21 from traitlets import List, Dict, default
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Magic implementation classes
24 # Magic implementation classes
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27 def script_args(f):
27 def script_args(f):
28 """single decorator for adding script args"""
28 """single decorator for adding script args"""
29 args = [
29 args = [
30 magic_arguments.argument(
30 magic_arguments.argument(
31 '--out', type=str,
31 '--out', type=str,
32 help="""The variable in which to store stdout from the script.
32 help="""The variable in which to store stdout from the script.
33 If the script is backgrounded, this will be the stdout *pipe*,
33 If the script is backgrounded, this will be the stdout *pipe*,
34 instead of the stderr text itself.
34 instead of the stderr text itself.
35 """
35 """
36 ),
36 ),
37 magic_arguments.argument(
37 magic_arguments.argument(
38 '--err', type=str,
38 '--err', type=str,
39 help="""The variable in which to store stderr from the script.
39 help="""The variable in which to store stderr from the script.
40 If the script is backgrounded, this will be the stderr *pipe*,
40 If the script is backgrounded, this will be the stderr *pipe*,
41 instead of the stderr text itself.
41 instead of the stderr text itself.
42 """
42 """
43 ),
43 ),
44 magic_arguments.argument(
44 magic_arguments.argument(
45 '--bg', action="store_true",
45 '--bg', action="store_true",
46 help="""Whether to run the script in the background.
46 help="""Whether to run the script in the background.
47 If given, the only way to see the output of the command is
47 If given, the only way to see the output of the command is
48 with --out/err.
48 with --out/err.
49 """
49 """
50 ),
50 ),
51 magic_arguments.argument(
51 magic_arguments.argument(
52 '--proc', type=str,
52 '--proc', type=str,
53 help="""The variable in which to store Popen instance.
53 help="""The variable in which to store Popen instance.
54 This is used only when --bg option is given.
54 This is used only when --bg option is given.
55 """
55 """
56 ),
56 ),
57 ]
57 ]
58 for arg in args:
58 for arg in args:
59 f = arg(f)
59 f = arg(f)
60 return f
60 return f
61
61
62 @magics_class
62 @magics_class
63 class ScriptMagics(Magics):
63 class ScriptMagics(Magics):
64 """Magics for talking to scripts
64 """Magics for talking to scripts
65
65
66 This defines a base `%%script` cell magic for running a cell
66 This defines a base `%%script` cell magic for running a cell
67 with a program in a subprocess, and registers a few top-level
67 with a program in a subprocess, and registers a few top-level
68 magics that call %%script with common interpreters.
68 magics that call %%script with common interpreters.
69 """
69 """
70 script_magics = List(
70 script_magics = List(
71 help="""Extra script cell magics to define
71 help="""Extra script cell magics to define
72
72
73 This generates simple wrappers of `%%script foo` as `%%foo`.
73 This generates simple wrappers of `%%script foo` as `%%foo`.
74
74
75 If you want to add script magics that aren't on your path,
75 If you want to add script magics that aren't on your path,
76 specify them in script_paths
76 specify them in script_paths
77 """,
77 """,
78 ).tag(config=True)
78 ).tag(config=True)
79 @default('script_magics')
79 @default('script_magics')
80 def _script_magics_default(self):
80 def _script_magics_default(self):
81 """default to a common list of programs"""
81 """default to a common list of programs"""
82
82
83 defaults = [
83 defaults = [
84 'sh',
84 'sh',
85 'bash',
85 'bash',
86 'perl',
86 'perl',
87 'ruby',
87 'ruby',
88 'python',
88 'python',
89 'python2',
89 'python2',
90 'python3',
90 'python3',
91 'pypy',
91 'pypy',
92 'julia',
93 ]
92 ]
94 if os.name == 'nt':
93 if os.name == 'nt':
95 defaults.extend([
94 defaults.extend([
96 'cmd',
95 'cmd',
97 ])
96 ])
98
97
99 return defaults
98 return defaults
100
99
101 script_paths = Dict(
100 script_paths = Dict(
102 help="""Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby'
101 help="""Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby'
103
102
104 Only necessary for items in script_magics where the default path will not
103 Only necessary for items in script_magics where the default path will not
105 find the right interpreter.
104 find the right interpreter.
106 """
105 """
107 ).tag(config=True)
106 ).tag(config=True)
108
107
109 def __init__(self, shell=None):
108 def __init__(self, shell=None):
110 super(ScriptMagics, self).__init__(shell=shell)
109 super(ScriptMagics, self).__init__(shell=shell)
111 self._generate_script_magics()
110 self._generate_script_magics()
112 self.job_manager = BackgroundJobManager()
111 self.job_manager = BackgroundJobManager()
113 self.bg_processes = []
112 self.bg_processes = []
114 atexit.register(self.kill_bg_processes)
113 atexit.register(self.kill_bg_processes)
115
114
116 def __del__(self):
115 def __del__(self):
117 self.kill_bg_processes()
116 self.kill_bg_processes()
118
117
119 def _generate_script_magics(self):
118 def _generate_script_magics(self):
120 cell_magics = self.magics['cell']
119 cell_magics = self.magics['cell']
121 for name in self.script_magics:
120 for name in self.script_magics:
122 cell_magics[name] = self._make_script_magic(name)
121 cell_magics[name] = self._make_script_magic(name)
123
122
124 def _make_script_magic(self, name):
123 def _make_script_magic(self, name):
125 """make a named magic, that calls %%script with a particular program"""
124 """make a named magic, that calls %%script with a particular program"""
126 # expand to explicit path if necessary:
125 # expand to explicit path if necessary:
127 script = self.script_paths.get(name, name)
126 script = self.script_paths.get(name, name)
128
127
129 @magic_arguments.magic_arguments()
128 @magic_arguments.magic_arguments()
130 @script_args
129 @script_args
131 def named_script_magic(line, cell):
130 def named_script_magic(line, cell):
132 # if line, add it as cl-flags
131 # if line, add it as cl-flags
133 if line:
132 if line:
134 line = "%s %s" % (script, line)
133 line = "%s %s" % (script, line)
135 else:
134 else:
136 line = script
135 line = script
137 return self.shebang(line, cell)
136 return self.shebang(line, cell)
138
137
139 # write a basic docstring:
138 # write a basic docstring:
140 named_script_magic.__doc__ = \
139 named_script_magic.__doc__ = \
141 """%%{name} script magic
140 """%%{name} script magic
142
141
143 Run cells with {script} in a subprocess.
142 Run cells with {script} in a subprocess.
144
143
145 This is a shortcut for `%%script {script}`
144 This is a shortcut for `%%script {script}`
146 """.format(**locals())
145 """.format(**locals())
147
146
148 return named_script_magic
147 return named_script_magic
149
148
150 @magic_arguments.magic_arguments()
149 @magic_arguments.magic_arguments()
151 @script_args
150 @script_args
152 @cell_magic("script")
151 @cell_magic("script")
153 def shebang(self, line, cell):
152 def shebang(self, line, cell):
154 """Run a cell via a shell command
153 """Run a cell via a shell command
155
154
156 The `%%script` line is like the #! line of script,
155 The `%%script` line is like the #! line of script,
157 specifying a program (bash, perl, ruby, etc.) with which to run.
156 specifying a program (bash, perl, ruby, etc.) with which to run.
158
157
159 The rest of the cell is run by that program.
158 The rest of the cell is run by that program.
160
159
161 Examples
160 Examples
162 --------
161 --------
163 ::
162 ::
164
163
165 In [1]: %%script bash
164 In [1]: %%script bash
166 ...: for i in 1 2 3; do
165 ...: for i in 1 2 3; do
167 ...: echo $i
166 ...: echo $i
168 ...: done
167 ...: done
169 1
168 1
170 2
169 2
171 3
170 3
172 """
171 """
173 argv = arg_split(line, posix = not sys.platform.startswith('win'))
172 argv = arg_split(line, posix = not sys.platform.startswith('win'))
174 args, cmd = self.shebang.parser.parse_known_args(argv)
173 args, cmd = self.shebang.parser.parse_known_args(argv)
175
174
176 try:
175 try:
177 p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE)
176 p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE)
178 except OSError as e:
177 except OSError as e:
179 if e.errno == errno.ENOENT:
178 if e.errno == errno.ENOENT:
180 print("Couldn't find program: %r" % cmd[0])
179 print("Couldn't find program: %r" % cmd[0])
181 return
180 return
182 else:
181 else:
183 raise
182 raise
184
183
185 if not cell.endswith('\n'):
184 if not cell.endswith('\n'):
186 cell += '\n'
185 cell += '\n'
187 cell = cell.encode('utf8', 'replace')
186 cell = cell.encode('utf8', 'replace')
188 if args.bg:
187 if args.bg:
189 self.bg_processes.append(p)
188 self.bg_processes.append(p)
190 self._gc_bg_processes()
189 self._gc_bg_processes()
191 if args.out:
190 if args.out:
192 self.shell.user_ns[args.out] = p.stdout
191 self.shell.user_ns[args.out] = p.stdout
193 if args.err:
192 if args.err:
194 self.shell.user_ns[args.err] = p.stderr
193 self.shell.user_ns[args.err] = p.stderr
195 self.job_manager.new(self._run_script, p, cell, daemon=True)
194 self.job_manager.new(self._run_script, p, cell, daemon=True)
196 if args.proc:
195 if args.proc:
197 self.shell.user_ns[args.proc] = p
196 self.shell.user_ns[args.proc] = p
198 return
197 return
199
198
200 try:
199 try:
201 out, err = p.communicate(cell)
200 out, err = p.communicate(cell)
202 except KeyboardInterrupt:
201 except KeyboardInterrupt:
203 try:
202 try:
204 p.send_signal(signal.SIGINT)
203 p.send_signal(signal.SIGINT)
205 time.sleep(0.1)
204 time.sleep(0.1)
206 if p.poll() is not None:
205 if p.poll() is not None:
207 print("Process is interrupted.")
206 print("Process is interrupted.")
208 return
207 return
209 p.terminate()
208 p.terminate()
210 time.sleep(0.1)
209 time.sleep(0.1)
211 if p.poll() is not None:
210 if p.poll() is not None:
212 print("Process is terminated.")
211 print("Process is terminated.")
213 return
212 return
214 p.kill()
213 p.kill()
215 print("Process is killed.")
214 print("Process is killed.")
216 except OSError:
215 except OSError:
217 pass
216 pass
218 except Exception as e:
217 except Exception as e:
219 print("Error while terminating subprocess (pid=%i): %s" \
218 print("Error while terminating subprocess (pid=%i): %s" \
220 % (p.pid, e))
219 % (p.pid, e))
221 return
220 return
222 out = py3compat.bytes_to_str(out)
221 out = py3compat.bytes_to_str(out)
223 err = py3compat.bytes_to_str(err)
222 err = py3compat.bytes_to_str(err)
224 if args.out:
223 if args.out:
225 self.shell.user_ns[args.out] = out
224 self.shell.user_ns[args.out] = out
226 else:
225 else:
227 sys.stdout.write(out)
226 sys.stdout.write(out)
228 sys.stdout.flush()
227 sys.stdout.flush()
229 if args.err:
228 if args.err:
230 self.shell.user_ns[args.err] = err
229 self.shell.user_ns[args.err] = err
231 else:
230 else:
232 sys.stderr.write(err)
231 sys.stderr.write(err)
233 sys.stderr.flush()
232 sys.stderr.flush()
234
233
235 def _run_script(self, p, cell):
234 def _run_script(self, p, cell):
236 """callback for running the script in the background"""
235 """callback for running the script in the background"""
237 p.stdin.write(cell)
236 p.stdin.write(cell)
238 p.stdin.close()
237 p.stdin.close()
239 p.wait()
238 p.wait()
240
239
241 @line_magic("killbgscripts")
240 @line_magic("killbgscripts")
242 def killbgscripts(self, _nouse_=''):
241 def killbgscripts(self, _nouse_=''):
243 """Kill all BG processes started by %%script and its family."""
242 """Kill all BG processes started by %%script and its family."""
244 self.kill_bg_processes()
243 self.kill_bg_processes()
245 print("All background processes were killed.")
244 print("All background processes were killed.")
246
245
247 def kill_bg_processes(self):
246 def kill_bg_processes(self):
248 """Kill all BG processes which are still running."""
247 """Kill all BG processes which are still running."""
249 if not self.bg_processes:
248 if not self.bg_processes:
250 return
249 return
251 for p in self.bg_processes:
250 for p in self.bg_processes:
252 if p.poll() is None:
251 if p.poll() is None:
253 try:
252 try:
254 p.send_signal(signal.SIGINT)
253 p.send_signal(signal.SIGINT)
255 except:
254 except:
256 pass
255 pass
257 time.sleep(0.1)
256 time.sleep(0.1)
258 self._gc_bg_processes()
257 self._gc_bg_processes()
259 if not self.bg_processes:
258 if not self.bg_processes:
260 return
259 return
261 for p in self.bg_processes:
260 for p in self.bg_processes:
262 if p.poll() is None:
261 if p.poll() is None:
263 try:
262 try:
264 p.terminate()
263 p.terminate()
265 except:
264 except:
266 pass
265 pass
267 time.sleep(0.1)
266 time.sleep(0.1)
268 self._gc_bg_processes()
267 self._gc_bg_processes()
269 if not self.bg_processes:
268 if not self.bg_processes:
270 return
269 return
271 for p in self.bg_processes:
270 for p in self.bg_processes:
272 if p.poll() is None:
271 if p.poll() is None:
273 try:
272 try:
274 p.kill()
273 p.kill()
275 except:
274 except:
276 pass
275 pass
277 self._gc_bg_processes()
276 self._gc_bg_processes()
278
277
279 def _gc_bg_processes(self):
278 def _gc_bg_processes(self):
280 self.bg_processes = [p for p in self.bg_processes if p.poll() is None]
279 self.bg_processes = [p for p in self.bg_processes if p.poll() is None]
General Comments 0
You need to be logged in to leave comments. Login now