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