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