Show More
@@ -0,0 +1,215 b'' | |||||
|
1 | """Magic functions for running cells in various scripts.""" | |||
|
2 | #----------------------------------------------------------------------------- | |||
|
3 | # Copyright (c) 2012 The IPython Development Team. | |||
|
4 | # | |||
|
5 | # Distributed under the terms of the Modified BSD License. | |||
|
6 | # | |||
|
7 | # The full license is in the file COPYING.txt, distributed with this software. | |||
|
8 | #----------------------------------------------------------------------------- | |||
|
9 | ||||
|
10 | #----------------------------------------------------------------------------- | |||
|
11 | # Imports | |||
|
12 | #----------------------------------------------------------------------------- | |||
|
13 | ||||
|
14 | # Stdlib | |||
|
15 | import os | |||
|
16 | import re | |||
|
17 | import sys | |||
|
18 | from subprocess import Popen, PIPE | |||
|
19 | ||||
|
20 | # Our own packages | |||
|
21 | from IPython.config.configurable import Configurable | |||
|
22 | from IPython.core import magic_arguments | |||
|
23 | from IPython.core.error import UsageError | |||
|
24 | from IPython.core.magic import ( | |||
|
25 | Magics, magics_class, line_magic, cell_magic | |||
|
26 | ) | |||
|
27 | from IPython.lib.backgroundjobs import BackgroundJobManager | |||
|
28 | from IPython.testing.skipdoctest import skip_doctest | |||
|
29 | from IPython.utils import py3compat | |||
|
30 | from IPython.utils.process import find_cmd, FindCmdError, arg_split | |||
|
31 | from IPython.utils.traitlets import List, Dict | |||
|
32 | ||||
|
33 | #----------------------------------------------------------------------------- | |||
|
34 | # Magic implementation classes | |||
|
35 | #----------------------------------------------------------------------------- | |||
|
36 | ||||
|
37 | def script_args(f): | |||
|
38 | """single decorator for adding script args""" | |||
|
39 | args = [ | |||
|
40 | magic_arguments.argument( | |||
|
41 | '--out', type=str, | |||
|
42 | help="""The variable in which to store stdout from the script. | |||
|
43 | If the script is backgrounded, this will be the stdout *pipe*, | |||
|
44 | instead of the stderr text itself. | |||
|
45 | """ | |||
|
46 | ), | |||
|
47 | magic_arguments.argument( | |||
|
48 | '--err', type=str, | |||
|
49 | help="""The variable in which to store stderr from the script. | |||
|
50 | If the script is backgrounded, this will be the stderr *pipe*, | |||
|
51 | instead of the stderr text itself. | |||
|
52 | """ | |||
|
53 | ), | |||
|
54 | magic_arguments.argument( | |||
|
55 | '--bg', action="store_true", | |||
|
56 | help="""Whether to run the script in the background. | |||
|
57 | If given, the only way to see the output of the command is | |||
|
58 | with --out/err. | |||
|
59 | """ | |||
|
60 | ), | |||
|
61 | ] | |||
|
62 | for arg in args: | |||
|
63 | f = arg(f) | |||
|
64 | return f | |||
|
65 | ||||
|
66 | @magics_class | |||
|
67 | class ScriptMagics(Magics, Configurable): | |||
|
68 | """Magics for talking to scripts | |||
|
69 | ||||
|
70 | This defines a base `%%script` cell magic for running a cell | |||
|
71 | with a program in a subprocess, and registers a few top-level | |||
|
72 | magics that call %%script with common interpreters. | |||
|
73 | """ | |||
|
74 | script_magics = List(config=True, | |||
|
75 | help="""Extra script cell magics to define | |||
|
76 | ||||
|
77 | This generates simple wrappers of `%%script foo` as `%%foo`. | |||
|
78 | ||||
|
79 | If you want to add script magics that aren't on your path, | |||
|
80 | specify them in script_paths | |||
|
81 | """, | |||
|
82 | ) | |||
|
83 | def _script_magics_default(self): | |||
|
84 | """default to a common list of programs if we find them""" | |||
|
85 | ||||
|
86 | defaults = [] | |||
|
87 | to_try = [] | |||
|
88 | if os.name == 'nt': | |||
|
89 | defaults.append('cmd') | |||
|
90 | to_try.append('powershell') | |||
|
91 | to_try.extend([ | |||
|
92 | 'sh', | |||
|
93 | 'bash', | |||
|
94 | 'perl', | |||
|
95 | 'ruby', | |||
|
96 | 'python3', | |||
|
97 | 'pypy', | |||
|
98 | ]) | |||
|
99 | ||||
|
100 | for cmd in to_try: | |||
|
101 | if cmd in self.script_paths: | |||
|
102 | defaults.append(cmd) | |||
|
103 | else: | |||
|
104 | try: | |||
|
105 | find_cmd(cmd) | |||
|
106 | except FindCmdError: | |||
|
107 | # command not found, ignore it | |||
|
108 | pass | |||
|
109 | except ImportError: | |||
|
110 | # Windows without pywin32, find_cmd doesn't work | |||
|
111 | pass | |||
|
112 | else: | |||
|
113 | defaults.append(cmd) | |||
|
114 | return defaults | |||
|
115 | ||||
|
116 | script_paths = Dict(config=True, | |||
|
117 | help="""Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby' | |||
|
118 | ||||
|
119 | Only necessary for items in script_magics where the default path will not | |||
|
120 | find the right interpreter. | |||
|
121 | """ | |||
|
122 | ) | |||
|
123 | ||||
|
124 | def __init__(self, shell=None): | |||
|
125 | Configurable.__init__(self, config=shell.config) | |||
|
126 | self._generate_script_magics() | |||
|
127 | Magics.__init__(self, shell=shell) | |||
|
128 | self.job_manager = BackgroundJobManager() | |||
|
129 | ||||
|
130 | def _generate_script_magics(self): | |||
|
131 | cell_magics = self.magics['cell'] | |||
|
132 | for name in self.script_magics: | |||
|
133 | cell_magics[name] = self._make_script_magic(name) | |||
|
134 | ||||
|
135 | def _make_script_magic(self, name): | |||
|
136 | """make a named magic, that calls %%script with a particular program""" | |||
|
137 | # expand to explicit path if necessary: | |||
|
138 | script = self.script_paths.get(name, name) | |||
|
139 | ||||
|
140 | @magic_arguments.magic_arguments() | |||
|
141 | @script_args | |||
|
142 | def named_script_magic(line, cell): | |||
|
143 | # if line, add it as cl-flags | |||
|
144 | if line: | |||
|
145 | line = "%s %s" % (script, line) | |||
|
146 | else: | |||
|
147 | line = script | |||
|
148 | return self.shebang(line, cell) | |||
|
149 | ||||
|
150 | # write a basic docstring: | |||
|
151 | named_script_magic.__doc__ = \ | |||
|
152 | """%%{name} script magic | |||
|
153 | ||||
|
154 | Run cells with {script} in a subprocess. | |||
|
155 | ||||
|
156 | This is a shortcut for `%%script {script}` | |||
|
157 | """.format(**locals()) | |||
|
158 | ||||
|
159 | return named_script_magic | |||
|
160 | ||||
|
161 | @magic_arguments.magic_arguments() | |||
|
162 | @script_args | |||
|
163 | @cell_magic("script") | |||
|
164 | def shebang(self, line, cell): | |||
|
165 | """Run a cell via a shell command | |||
|
166 | ||||
|
167 | The `%%script` line is like the #! line of script, | |||
|
168 | specifying a program (bash, perl, ruby, etc.) with which to run. | |||
|
169 | ||||
|
170 | The rest of the cell is run by that program. | |||
|
171 | ||||
|
172 | Examples | |||
|
173 | -------- | |||
|
174 | :: | |||
|
175 | ||||
|
176 | In [1]: %%script bash | |||
|
177 | ...: for i in 1 2 3; do | |||
|
178 | ...: echo $i | |||
|
179 | ...: done | |||
|
180 | 1 | |||
|
181 | 2 | |||
|
182 | 3 | |||
|
183 | """ | |||
|
184 | argv = arg_split(line, posix = not sys.platform.startswith('win')) | |||
|
185 | args, cmd = self.shebang.parser.parse_known_args(argv) | |||
|
186 | ||||
|
187 | p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE) | |||
|
188 | ||||
|
189 | if args.bg: | |||
|
190 | if args.out: | |||
|
191 | self.shell.user_ns[args.out] = p.stdout | |||
|
192 | if args.err: | |||
|
193 | self.shell.user_ns[args.err] = p.stderr | |||
|
194 | self.job_manager.new(self._run_script, p, cell) | |||
|
195 | return | |||
|
196 | ||||
|
197 | out, err = p.communicate(cell) | |||
|
198 | out = py3compat.bytes_to_str(out) | |||
|
199 | err = py3compat.bytes_to_str(err) | |||
|
200 | if args.out: | |||
|
201 | self.shell.user_ns[args.out] = out | |||
|
202 | else: | |||
|
203 | sys.stdout.write(out) | |||
|
204 | sys.stdout.flush() | |||
|
205 | if args.err: | |||
|
206 | self.shell.user_ns[args.err] = err | |||
|
207 | else: | |||
|
208 | sys.stderr.write(err) | |||
|
209 | sys.stderr.flush() | |||
|
210 | ||||
|
211 | def _run_script(self, p, cell): | |||
|
212 | """callback for running the script in the background""" | |||
|
213 | p.stdin.write(cell) | |||
|
214 | p.stdin.close() | |||
|
215 | p.wait() |
@@ -0,0 +1,448 b'' | |||||
|
1 | { | |||
|
2 | "metadata": { | |||
|
3 | "name": "Script Magics" | |||
|
4 | }, | |||
|
5 | "nbformat": 3, | |||
|
6 | "worksheets": [ | |||
|
7 | { | |||
|
8 | "cells": [ | |||
|
9 | { | |||
|
10 | "cell_type": "heading", | |||
|
11 | "level": 1, | |||
|
12 | "source": [ | |||
|
13 | "Running Scripts from IPython" | |||
|
14 | ] | |||
|
15 | }, | |||
|
16 | { | |||
|
17 | "cell_type": "markdown", | |||
|
18 | "source": [ | |||
|
19 | "IPython has a `%%script` cell magic, which lets you run a cell in", | |||
|
20 | "a subprocess of any interpreter on your system, such as: bash, ruby, perl, zsh, R, etc.", | |||
|
21 | "", | |||
|
22 | "It can even be a script of your own, which expects input on stdin." | |||
|
23 | ] | |||
|
24 | }, | |||
|
25 | { | |||
|
26 | "cell_type": "code", | |||
|
27 | "input": [ | |||
|
28 | "import sys" | |||
|
29 | ], | |||
|
30 | "language": "python", | |||
|
31 | "outputs": [], | |||
|
32 | "prompt_number": 1 | |||
|
33 | }, | |||
|
34 | { | |||
|
35 | "cell_type": "markdown", | |||
|
36 | "source": [ | |||
|
37 | "To use it, simply pass a path or shell command to the program you want to run on the `%%script` line,", | |||
|
38 | "and the rest of the cell will be run by that script, and stdout/err from the subprocess are captured and displayed." | |||
|
39 | ] | |||
|
40 | }, | |||
|
41 | { | |||
|
42 | "cell_type": "code", | |||
|
43 | "input": [ | |||
|
44 | "%%script python", | |||
|
45 | "import sys", | |||
|
46 | "print 'hello from Python %s' % sys.version" | |||
|
47 | ], | |||
|
48 | "language": "python", | |||
|
49 | "outputs": [ | |||
|
50 | { | |||
|
51 | "output_type": "stream", | |||
|
52 | "stream": "stdout", | |||
|
53 | "text": [ | |||
|
54 | "hello from Python 2.7.1 (r271:86832, Jul 31 2011, 19:30:53) ", | |||
|
55 | "[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)]", | |||
|
56 | "" | |||
|
57 | ] | |||
|
58 | } | |||
|
59 | ], | |||
|
60 | "prompt_number": 2 | |||
|
61 | }, | |||
|
62 | { | |||
|
63 | "cell_type": "code", | |||
|
64 | "input": [ | |||
|
65 | "%%script python3", | |||
|
66 | "import sys", | |||
|
67 | "print('hello from Python: %s' % sys.version)" | |||
|
68 | ], | |||
|
69 | "language": "python", | |||
|
70 | "outputs": [ | |||
|
71 | { | |||
|
72 | "output_type": "stream", | |||
|
73 | "stream": "stdout", | |||
|
74 | "text": [ | |||
|
75 | "hello from Python: 3.2.3 (v3.2.3:3d0686d90f55, Apr 10 2012, 11:25:50) ", | |||
|
76 | "[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]", | |||
|
77 | "" | |||
|
78 | ] | |||
|
79 | } | |||
|
80 | ], | |||
|
81 | "prompt_number": 3 | |||
|
82 | }, | |||
|
83 | { | |||
|
84 | "cell_type": "markdown", | |||
|
85 | "source": [ | |||
|
86 | "IPython also creates aliases for a few common interpreters, such as bash, ruby, perl, etc.", | |||
|
87 | "", | |||
|
88 | "These are all equivalent to `%%script <name>`" | |||
|
89 | ] | |||
|
90 | }, | |||
|
91 | { | |||
|
92 | "cell_type": "code", | |||
|
93 | "input": [ | |||
|
94 | "%%ruby", | |||
|
95 | "puts \"Hello from Ruby #{RUBY_VERSION}\"" | |||
|
96 | ], | |||
|
97 | "language": "python", | |||
|
98 | "outputs": [ | |||
|
99 | { | |||
|
100 | "output_type": "stream", | |||
|
101 | "stream": "stdout", | |||
|
102 | "text": [ | |||
|
103 | "Hello from Ruby 1.8.7", | |||
|
104 | "" | |||
|
105 | ] | |||
|
106 | } | |||
|
107 | ], | |||
|
108 | "prompt_number": 4 | |||
|
109 | }, | |||
|
110 | { | |||
|
111 | "cell_type": "code", | |||
|
112 | "input": [ | |||
|
113 | "%%bash", | |||
|
114 | "echo \"hello from $BASH\"" | |||
|
115 | ], | |||
|
116 | "language": "python", | |||
|
117 | "outputs": [ | |||
|
118 | { | |||
|
119 | "output_type": "stream", | |||
|
120 | "stream": "stdout", | |||
|
121 | "text": [ | |||
|
122 | "hello from /usr/local/bin/bash", | |||
|
123 | "" | |||
|
124 | ] | |||
|
125 | } | |||
|
126 | ], | |||
|
127 | "prompt_number": 5 | |||
|
128 | }, | |||
|
129 | { | |||
|
130 | "cell_type": "heading", | |||
|
131 | "level": 2, | |||
|
132 | "source": [ | |||
|
133 | "Capturing output" | |||
|
134 | ] | |||
|
135 | }, | |||
|
136 | { | |||
|
137 | "cell_type": "markdown", | |||
|
138 | "source": [ | |||
|
139 | "You can also capture stdout/err from these subprocesses into Python variables, instead of letting them go directly to stdout/err" | |||
|
140 | ] | |||
|
141 | }, | |||
|
142 | { | |||
|
143 | "cell_type": "code", | |||
|
144 | "input": [ | |||
|
145 | "%%bash", | |||
|
146 | "echo \"hi, stdout\"", | |||
|
147 | "echo \"hello, stderr\" >&2", | |||
|
148 | "" | |||
|
149 | ], | |||
|
150 | "language": "python", | |||
|
151 | "outputs": [ | |||
|
152 | { | |||
|
153 | "output_type": "stream", | |||
|
154 | "stream": "stdout", | |||
|
155 | "text": [ | |||
|
156 | "hi, stdout", | |||
|
157 | "" | |||
|
158 | ] | |||
|
159 | }, | |||
|
160 | { | |||
|
161 | "output_type": "stream", | |||
|
162 | "stream": "stderr", | |||
|
163 | "text": [ | |||
|
164 | "hello, stderr", | |||
|
165 | "" | |||
|
166 | ] | |||
|
167 | } | |||
|
168 | ], | |||
|
169 | "prompt_number": 6 | |||
|
170 | }, | |||
|
171 | { | |||
|
172 | "cell_type": "code", | |||
|
173 | "input": [ | |||
|
174 | "%%bash --out output --err error", | |||
|
175 | "echo \"hi, stdout\"", | |||
|
176 | "echo \"hello, stderr\" >&2" | |||
|
177 | ], | |||
|
178 | "language": "python", | |||
|
179 | "outputs": [], | |||
|
180 | "prompt_number": 7 | |||
|
181 | }, | |||
|
182 | { | |||
|
183 | "cell_type": "code", | |||
|
184 | "input": [ | |||
|
185 | "print error", | |||
|
186 | "print output" | |||
|
187 | ], | |||
|
188 | "language": "python", | |||
|
189 | "outputs": [ | |||
|
190 | { | |||
|
191 | "output_type": "stream", | |||
|
192 | "stream": "stdout", | |||
|
193 | "text": [ | |||
|
194 | "hello, stderr", | |||
|
195 | "", | |||
|
196 | "hi, stdout", | |||
|
197 | "", | |||
|
198 | "" | |||
|
199 | ] | |||
|
200 | } | |||
|
201 | ], | |||
|
202 | "prompt_number": 8 | |||
|
203 | }, | |||
|
204 | { | |||
|
205 | "cell_type": "heading", | |||
|
206 | "level": 2, | |||
|
207 | "source": [ | |||
|
208 | "Background Scripts" | |||
|
209 | ] | |||
|
210 | }, | |||
|
211 | { | |||
|
212 | "cell_type": "markdown", | |||
|
213 | "source": [ | |||
|
214 | "These scripts can be run in the background, by adding the `--bg` flag.", | |||
|
215 | "", | |||
|
216 | "When you do this, output is discarded unless you use the `--out/err`", | |||
|
217 | "flags to store output as above." | |||
|
218 | ] | |||
|
219 | }, | |||
|
220 | { | |||
|
221 | "cell_type": "code", | |||
|
222 | "input": [ | |||
|
223 | "%%ruby --bg --out ruby_lines", | |||
|
224 | "for n in 1...10", | |||
|
225 | " sleep 1", | |||
|
226 | " puts \"line #{n}\"", | |||
|
227 | " STDOUT.flush", | |||
|
228 | "end" | |||
|
229 | ], | |||
|
230 | "language": "python", | |||
|
231 | "outputs": [ | |||
|
232 | { | |||
|
233 | "output_type": "stream", | |||
|
234 | "stream": "stdout", | |||
|
235 | "text": [ | |||
|
236 | "Starting job # 0 in a separate thread.", | |||
|
237 | "" | |||
|
238 | ] | |||
|
239 | } | |||
|
240 | ], | |||
|
241 | "prompt_number": 9 | |||
|
242 | }, | |||
|
243 | { | |||
|
244 | "cell_type": "markdown", | |||
|
245 | "source": [ | |||
|
246 | "When you do store output of a background thread, these are the stdout/err *pipes*,", | |||
|
247 | "rather than the text of the output." | |||
|
248 | ] | |||
|
249 | }, | |||
|
250 | { | |||
|
251 | "cell_type": "code", | |||
|
252 | "input": [ | |||
|
253 | "ruby_lines" | |||
|
254 | ], | |||
|
255 | "language": "python", | |||
|
256 | "outputs": [ | |||
|
257 | { | |||
|
258 | "output_type": "pyout", | |||
|
259 | "prompt_number": 10, | |||
|
260 | "text": [ | |||
|
261 | "<open file '<fdopen>', mode 'rb' at 0x10dc651e0>" | |||
|
262 | ] | |||
|
263 | } | |||
|
264 | ], | |||
|
265 | "prompt_number": 10 | |||
|
266 | }, | |||
|
267 | { | |||
|
268 | "cell_type": "code", | |||
|
269 | "input": [ | |||
|
270 | "print ruby_lines.read()" | |||
|
271 | ], | |||
|
272 | "language": "python", | |||
|
273 | "outputs": [ | |||
|
274 | { | |||
|
275 | "output_type": "stream", | |||
|
276 | "stream": "stdout", | |||
|
277 | "text": [ | |||
|
278 | "line 1", | |||
|
279 | "line 2", | |||
|
280 | "line 3", | |||
|
281 | "line 4", | |||
|
282 | "line 5", | |||
|
283 | "line 6", | |||
|
284 | "line 7", | |||
|
285 | "line 8", | |||
|
286 | "line 9", | |||
|
287 | "", | |||
|
288 | "" | |||
|
289 | ] | |||
|
290 | } | |||
|
291 | ], | |||
|
292 | "prompt_number": 11 | |||
|
293 | }, | |||
|
294 | { | |||
|
295 | "cell_type": "heading", | |||
|
296 | "level": 2, | |||
|
297 | "source": [ | |||
|
298 | "Arguments to subcommand" | |||
|
299 | ] | |||
|
300 | }, | |||
|
301 | { | |||
|
302 | "cell_type": "markdown", | |||
|
303 | "source": [ | |||
|
304 | "You can pass arguments the subcommand as well,", | |||
|
305 | "such as this example instructing Python to use integer division from Python 3:" | |||
|
306 | ] | |||
|
307 | }, | |||
|
308 | { | |||
|
309 | "cell_type": "code", | |||
|
310 | "input": [ | |||
|
311 | "%%script python -Qnew", | |||
|
312 | "print 1/3" | |||
|
313 | ], | |||
|
314 | "language": "python", | |||
|
315 | "outputs": [ | |||
|
316 | { | |||
|
317 | "output_type": "stream", | |||
|
318 | "stream": "stdout", | |||
|
319 | "text": [ | |||
|
320 | "0.333333333333", | |||
|
321 | "" | |||
|
322 | ] | |||
|
323 | } | |||
|
324 | ], | |||
|
325 | "prompt_number": 12 | |||
|
326 | }, | |||
|
327 | { | |||
|
328 | "cell_type": "markdown", | |||
|
329 | "source": [ | |||
|
330 | "You can really specify *any* program for `%%script`,", | |||
|
331 | "for instance here is a 'program' that echos the lines of stdin, with delays between each line." | |||
|
332 | ] | |||
|
333 | }, | |||
|
334 | { | |||
|
335 | "cell_type": "code", | |||
|
336 | "input": [ | |||
|
337 | "%%script --bg --out bashout bash -c \"while read line; do echo $line; sleep 1; done\"", | |||
|
338 | "line 1", | |||
|
339 | "line 2", | |||
|
340 | "line 3", | |||
|
341 | "line 4", | |||
|
342 | "line 5", | |||
|
343 | "" | |||
|
344 | ], | |||
|
345 | "language": "python", | |||
|
346 | "outputs": [ | |||
|
347 | { | |||
|
348 | "output_type": "stream", | |||
|
349 | "stream": "stdout", | |||
|
350 | "text": [ | |||
|
351 | "Starting job # 2 in a separate thread.", | |||
|
352 | "" | |||
|
353 | ] | |||
|
354 | } | |||
|
355 | ], | |||
|
356 | "prompt_number": 13 | |||
|
357 | }, | |||
|
358 | { | |||
|
359 | "cell_type": "markdown", | |||
|
360 | "source": [ | |||
|
361 | "Remember, since the output of a background script is just the stdout pipe,", | |||
|
362 | "you can read it as lines become available:" | |||
|
363 | ] | |||
|
364 | }, | |||
|
365 | { | |||
|
366 | "cell_type": "code", | |||
|
367 | "input": [ | |||
|
368 | "import time", | |||
|
369 | "tic = time.time()", | |||
|
370 | "line = True", | |||
|
371 | "while True:", | |||
|
372 | " line = bashout.readline()", | |||
|
373 | " if not line:", | |||
|
374 | " break", | |||
|
375 | " sys.stdout.write(\"%.1fs: %s\" %(time.time()-tic, line))", | |||
|
376 | " sys.stdout.flush()", | |||
|
377 | "" | |||
|
378 | ], | |||
|
379 | "language": "python", | |||
|
380 | "outputs": [ | |||
|
381 | { | |||
|
382 | "output_type": "stream", | |||
|
383 | "stream": "stdout", | |||
|
384 | "text": [ | |||
|
385 | "0.0s: line 1", | |||
|
386 | "" | |||
|
387 | ] | |||
|
388 | }, | |||
|
389 | { | |||
|
390 | "output_type": "stream", | |||
|
391 | "stream": "stdout", | |||
|
392 | "text": [ | |||
|
393 | "1.0s: line 2", | |||
|
394 | "" | |||
|
395 | ] | |||
|
396 | }, | |||
|
397 | { | |||
|
398 | "output_type": "stream", | |||
|
399 | "stream": "stdout", | |||
|
400 | "text": [ | |||
|
401 | "2.0s: line 3", | |||
|
402 | "" | |||
|
403 | ] | |||
|
404 | }, | |||
|
405 | { | |||
|
406 | "output_type": "stream", | |||
|
407 | "stream": "stdout", | |||
|
408 | "text": [ | |||
|
409 | "3.0s: line 4", | |||
|
410 | "" | |||
|
411 | ] | |||
|
412 | }, | |||
|
413 | { | |||
|
414 | "output_type": "stream", | |||
|
415 | "stream": "stdout", | |||
|
416 | "text": [ | |||
|
417 | "4.0s: line 5", | |||
|
418 | "" | |||
|
419 | ] | |||
|
420 | } | |||
|
421 | ], | |||
|
422 | "prompt_number": 14 | |||
|
423 | }, | |||
|
424 | { | |||
|
425 | "cell_type": "heading", | |||
|
426 | "level": 2, | |||
|
427 | "source": [ | |||
|
428 | "Configuring the default ScriptMagics" | |||
|
429 | ] | |||
|
430 | }, | |||
|
431 | { | |||
|
432 | "cell_type": "markdown", | |||
|
433 | "source": [ | |||
|
434 | "The list of aliased script magics is configurable.", | |||
|
435 | "", | |||
|
436 | "The default is to pick from a few common interpreters, and use them if found, but you can specify your own in ipython_config.py:", | |||
|
437 | "", | |||
|
438 | " c.ScriptMagics.scripts = ['R', 'pypy', 'myprogram']", | |||
|
439 | "", | |||
|
440 | "And if any of these programs do not apear on your default PATH, then you would also need to specify their location with:", | |||
|
441 | "", | |||
|
442 | " c.ScriptMagics.script_paths = {'myprogram': '/opt/path/to/myprogram'}" | |||
|
443 | ] | |||
|
444 | } | |||
|
445 | ] | |||
|
446 | } | |||
|
447 | ] | |||
|
448 | } No newline at end of file |
@@ -2017,7 +2017,8 b' class InteractiveShell(SingletonConfigurable):' | |||||
2017 | self.register_magics(m.AutoMagics, m.BasicMagics, m.CodeMagics, |
|
2017 | self.register_magics(m.AutoMagics, m.BasicMagics, m.CodeMagics, | |
2018 | m.ConfigMagics, m.DeprecatedMagics, m.ExecutionMagics, |
|
2018 | m.ConfigMagics, m.DeprecatedMagics, m.ExecutionMagics, | |
2019 | m.ExtensionMagics, m.HistoryMagics, m.LoggingMagics, |
|
2019 | m.ExtensionMagics, m.HistoryMagics, m.LoggingMagics, | |
2020 |
m.NamespaceMagics, m.OSMagics, m.PylabMagics |
|
2020 | m.NamespaceMagics, m.OSMagics, m.PylabMagics, m.ScriptMagics, | |
|
2021 | ) | |||
2021 |
|
2022 | |||
2022 | # FIXME: Move the color initialization to the DisplayHook, which |
|
2023 | # FIXME: Move the color initialization to the DisplayHook, which | |
2023 | # should be split into a prompt manager and displayhook. We probably |
|
2024 | # should be split into a prompt manager and displayhook. We probably |
@@ -30,7 +30,7 b' from IPython.external.decorator import decorator' | |||||
30 | from IPython.utils.ipstruct import Struct |
|
30 | from IPython.utils.ipstruct import Struct | |
31 | from IPython.utils.process import arg_split |
|
31 | from IPython.utils.process import arg_split | |
32 | from IPython.utils.text import dedent |
|
32 | from IPython.utils.text import dedent | |
33 | from IPython.utils.traitlets import Bool, Dict, Instance |
|
33 | from IPython.utils.traitlets import Bool, Dict, Instance, MetaHasTraits | |
34 | from IPython.utils.warn import error, warn |
|
34 | from IPython.utils.warn import error, warn | |
35 |
|
35 | |||
36 | #----------------------------------------------------------------------------- |
|
36 | #----------------------------------------------------------------------------- | |
@@ -365,10 +365,10 b' class MagicsManager(Configurable):' | |||||
365 | for m in magic_objects: |
|
365 | for m in magic_objects: | |
366 | if not m.registered: |
|
366 | if not m.registered: | |
367 | raise ValueError("Class of magics %r was constructed without " |
|
367 | raise ValueError("Class of magics %r was constructed without " | |
368 |
"the @register_ma |
|
368 | "the @register_magics class decorator") | |
369 |
if type(m) i |
|
369 | if type(m) in (type, MetaHasTraits): | |
370 | # If we're given an uninstantiated class |
|
370 | # If we're given an uninstantiated class | |
371 | m = m(self.shell) |
|
371 | m = m(shell=self.shell) | |
372 |
|
372 | |||
373 | # Now that we have an instance, we can register it and update the |
|
373 | # Now that we have an instance, we can register it and update the | |
374 | # table of callables |
|
374 | # table of callables |
@@ -25,6 +25,7 b' from .logging import LoggingMagics' | |||||
25 | from .namespace import NamespaceMagics |
|
25 | from .namespace import NamespaceMagics | |
26 | from .osm import OSMagics |
|
26 | from .osm import OSMagics | |
27 | from .pylab import PylabMagics |
|
27 | from .pylab import PylabMagics | |
|
28 | from .script import ScriptMagics | |||
28 |
|
29 | |||
29 | #----------------------------------------------------------------------------- |
|
30 | #----------------------------------------------------------------------------- | |
30 | # Magic implementation classes |
|
31 | # Magic implementation classes |
@@ -16,17 +16,20 b' builtin.' | |||||
16 | #----------------------------------------------------------------------------- |
|
16 | #----------------------------------------------------------------------------- | |
17 |
|
17 | |||
18 | # Stdlib |
|
18 | # Stdlib | |
|
19 | import io | |||
19 | import os |
|
20 | import os | |
20 | import re |
|
21 | import re | |
21 | import sys |
|
22 | import sys | |
22 | from pprint import pformat |
|
23 | from pprint import pformat | |
23 |
|
24 | |||
24 | # Our own packages |
|
25 | # Our own packages | |
|
26 | from IPython.core import magic_arguments | |||
25 | from IPython.core import oinspect |
|
27 | from IPython.core import oinspect | |
26 | from IPython.core import page |
|
28 | from IPython.core import page | |
27 | from IPython.core.error import UsageError |
|
29 | from IPython.core.error import UsageError, StdinNotImplementedError | |
28 |
from IPython.core.magic import ( |
|
30 | from IPython.core.magic import ( | |
29 | line_magic) |
|
31 | Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic | |
|
32 | ) | |||
30 | from IPython.testing.skipdoctest import skip_doctest |
|
33 | from IPython.testing.skipdoctest import skip_doctest | |
31 | from IPython.utils.io import file_read, nlprint |
|
34 | from IPython.utils.io import file_read, nlprint | |
32 | from IPython.utils.path import get_py_filename, unquote_filename |
|
35 | from IPython.utils.path import get_py_filename, unquote_filename | |
@@ -432,7 +435,7 b' class OSMagics(Magics):' | |||||
432 | @skip_doctest |
|
435 | @skip_doctest | |
433 | @line_magic |
|
436 | @line_magic | |
434 | def sc(self, parameter_s=''): |
|
437 | def sc(self, parameter_s=''): | |
435 |
"""Shell capture - |
|
438 | """Shell capture - run shell command and capture output (DEPRECATED use !). | |
436 |
|
439 | |||
437 | DEPRECATED. Suboptimal, retained for backwards compatibility. |
|
440 | DEPRECATED. Suboptimal, retained for backwards compatibility. | |
438 |
|
441 | |||
@@ -545,9 +548,9 b' class OSMagics(Magics):' | |||||
545 | else: |
|
548 | else: | |
546 | return out |
|
549 | return out | |
547 |
|
550 | |||
548 | @line_magic |
|
551 | @line_cell_magic | |
549 |
def sx(self, |
|
552 | def sx(self, line='', cell=None): | |
550 |
"""Shell execute - run |
|
553 | """Shell execute - run shell command and capture output (!! is short-hand). | |
551 |
|
554 | |||
552 | %sx command |
|
555 | %sx command | |
553 |
|
556 | |||
@@ -586,10 +589,21 b' class OSMagics(Magics):' | |||||
586 |
|
589 | |||
587 | This is very useful when trying to use such lists as arguments to |
|
590 | This is very useful when trying to use such lists as arguments to | |
588 | system commands.""" |
|
591 | system commands.""" | |
|
592 | ||||
|
593 | if cell is None: | |||
|
594 | # line magic | |||
|
595 | return self.shell.getoutput(line) | |||
|
596 | else: | |||
|
597 | opts,args = self.parse_options(line, '', 'out=') | |||
|
598 | output = self.shell.getoutput(cell) | |||
|
599 | out_name = opts.get('out', opts.get('o')) | |||
|
600 | if out_name: | |||
|
601 | self.shell.user_ns[out_name] = output | |||
|
602 | else: | |||
|
603 | return output | |||
589 |
|
604 | |||
590 | if parameter_s: |
|
605 | system = line_cell_magic('system')(sx) | |
591 | return self.shell.getoutput(parameter_s) |
|
606 | bang = cell_magic('!')(sx) | |
592 |
|
||||
593 |
|
607 | |||
594 | @line_magic |
|
608 | @line_magic | |
595 | def bookmark(self, parameter_s=''): |
|
609 | def bookmark(self, parameter_s=''): | |
@@ -675,3 +689,33 b' class OSMagics(Magics):' | |||||
675 | return |
|
689 | return | |
676 |
|
690 | |||
677 | page.page(self.shell.pycolorize(cont)) |
|
691 | page.page(self.shell.pycolorize(cont)) | |
|
692 | ||||
|
693 | @magic_arguments.magic_arguments() | |||
|
694 | @magic_arguments.argument( | |||
|
695 | '-a', '--amend', action='store_true', default=False, | |||
|
696 | help='Open file for amending if it exists' | |||
|
697 | ) | |||
|
698 | @magic_arguments.argument( | |||
|
699 | 'filename', type=unicode, | |||
|
700 | help='file to write' | |||
|
701 | ) | |||
|
702 | @cell_magic | |||
|
703 | def file(self, line, cell): | |||
|
704 | """Write the contents of the cell to a file. | |||
|
705 | ||||
|
706 | For frontends that do not support stdin (Notebook), -f is implied. | |||
|
707 | """ | |||
|
708 | args = magic_arguments.parse_argstring(self.file, line) | |||
|
709 | filename = unquote_filename(args.filename) | |||
|
710 | ||||
|
711 | if os.path.exists(filename): | |||
|
712 | if args.amend: | |||
|
713 | print "Amending to %s" % filename | |||
|
714 | else: | |||
|
715 | print "Overwriting %s" % filename | |||
|
716 | else: | |||
|
717 | print "Writing %s" % filename | |||
|
718 | ||||
|
719 | mode = 'a' if args.amend else 'w' | |||
|
720 | with io.open(filename, mode, encoding='utf-8') as f: | |||
|
721 | f.write(cell) |
@@ -28,13 +28,14 b' from IPython.core.magic import (Magics, magics_class, line_magic,' | |||||
28 | cell_magic, line_cell_magic, |
|
28 | cell_magic, line_cell_magic, | |
29 | register_line_magic, register_cell_magic, |
|
29 | register_line_magic, register_cell_magic, | |
30 | register_line_cell_magic) |
|
30 | register_line_cell_magic) | |
31 | from IPython.core.magics import execution |
|
31 | from IPython.core.magics import execution, script | |
32 | from IPython.nbformat.v3.tests.nbexamples import nb0 |
|
32 | from IPython.nbformat.v3.tests.nbexamples import nb0 | |
33 | from IPython.nbformat import current |
|
33 | from IPython.nbformat import current | |
34 | from IPython.testing import decorators as dec |
|
34 | from IPython.testing import decorators as dec | |
35 | from IPython.testing import tools as tt |
|
35 | from IPython.testing import tools as tt | |
36 | from IPython.utils import py3compat |
|
36 | from IPython.utils import py3compat | |
37 | from IPython.utils.tempdir import TemporaryDirectory |
|
37 | from IPython.utils.tempdir import TemporaryDirectory | |
|
38 | from IPython.utils.process import find_cmd | |||
38 |
|
39 | |||
39 | #----------------------------------------------------------------------------- |
|
40 | #----------------------------------------------------------------------------- | |
40 | # Test functions begin |
|
41 | # Test functions begin | |
@@ -560,4 +561,104 b' class CellMagicTestCase(TestCase):' | |||||
560 | # Check that nothing is registered as 'cellm33' |
|
561 | # Check that nothing is registered as 'cellm33' | |
561 | c33 = _ip.find_cell_magic('cellm33') |
|
562 | c33 = _ip.find_cell_magic('cellm33') | |
562 | nt.assert_equals(c33, None) |
|
563 | nt.assert_equals(c33, None) | |
|
564 | ||||
|
565 | def test_file(): | |||
|
566 | """Basic %%file""" | |||
|
567 | ip = get_ipython() | |||
|
568 | with TemporaryDirectory() as td: | |||
|
569 | fname = os.path.join(td, 'file1') | |||
|
570 | ip.run_cell_magic("file", fname, u'\n'.join([ | |||
|
571 | 'line1', | |||
|
572 | 'line2', | |||
|
573 | ])) | |||
|
574 | with open(fname) as f: | |||
|
575 | s = f.read() | |||
|
576 | nt.assert_in('line1\n', s) | |||
|
577 | nt.assert_in('line2', s) | |||
|
578 | ||||
|
579 | def test_file_unicode(): | |||
|
580 | """%%file with unicode cell""" | |||
|
581 | ip = get_ipython() | |||
|
582 | with TemporaryDirectory() as td: | |||
|
583 | fname = os.path.join(td, 'file1') | |||
|
584 | ip.run_cell_magic("file", fname, u'\n'.join([ | |||
|
585 | u'linΓ©1', | |||
|
586 | u'linΓ©2', | |||
|
587 | ])) | |||
|
588 | with io.open(fname, encoding='utf-8') as f: | |||
|
589 | s = f.read() | |||
|
590 | nt.assert_in(u'linΓ©1\n', s) | |||
|
591 | nt.assert_in(u'linΓ©2', s) | |||
|
592 | ||||
|
593 | def test_file_amend(): | |||
|
594 | """%%file -a amends files""" | |||
|
595 | ip = get_ipython() | |||
|
596 | with TemporaryDirectory() as td: | |||
|
597 | fname = os.path.join(td, 'file2') | |||
|
598 | ip.run_cell_magic("file", fname, u'\n'.join([ | |||
|
599 | 'line1', | |||
|
600 | 'line2', | |||
|
601 | ])) | |||
|
602 | ip.run_cell_magic("file", "-a %s" % fname, u'\n'.join([ | |||
|
603 | 'line3', | |||
|
604 | 'line4', | |||
|
605 | ])) | |||
|
606 | with open(fname) as f: | |||
|
607 | s = f.read() | |||
|
608 | nt.assert_in('line1\n', s) | |||
|
609 | nt.assert_in('line3\n', s) | |||
|
610 | ||||
563 |
|
611 | |||
|
612 | def test_script_config(): | |||
|
613 | ip = get_ipython() | |||
|
614 | ip.config.ScriptMagics.script_magics = ['whoda'] | |||
|
615 | sm = script.ScriptMagics(shell=ip) | |||
|
616 | nt.assert_in('whoda', sm.magics['cell']) | |||
|
617 | ||||
|
618 | @dec.skip_win32 | |||
|
619 | def test_script_out(): | |||
|
620 | ip = get_ipython() | |||
|
621 | ip.run_cell_magic("script", "--out output sh", "echo 'hi'") | |||
|
622 | nt.assert_equals(ip.user_ns['output'], 'hi\n') | |||
|
623 | ||||
|
624 | @dec.skip_win32 | |||
|
625 | def test_script_err(): | |||
|
626 | ip = get_ipython() | |||
|
627 | ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2") | |||
|
628 | nt.assert_equals(ip.user_ns['error'], 'hello\n') | |||
|
629 | ||||
|
630 | @dec.skip_win32 | |||
|
631 | def test_script_out_err(): | |||
|
632 | ip = get_ipython() | |||
|
633 | ip.run_cell_magic("script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2") | |||
|
634 | nt.assert_equals(ip.user_ns['output'], 'hi\n') | |||
|
635 | nt.assert_equals(ip.user_ns['error'], 'hello\n') | |||
|
636 | ||||
|
637 | @dec.skip_win32 | |||
|
638 | def test_script_bg_out(): | |||
|
639 | ip = get_ipython() | |||
|
640 | ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'") | |||
|
641 | nt.assert_equals(ip.user_ns['output'].read(), 'hi\n') | |||
|
642 | ||||
|
643 | @dec.skip_win32 | |||
|
644 | def test_script_bg_err(): | |||
|
645 | ip = get_ipython() | |||
|
646 | ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2") | |||
|
647 | nt.assert_equals(ip.user_ns['error'].read(), 'hello\n') | |||
|
648 | ||||
|
649 | @dec.skip_win32 | |||
|
650 | def test_script_bg_out_err(): | |||
|
651 | ip = get_ipython() | |||
|
652 | ip.run_cell_magic("script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2") | |||
|
653 | nt.assert_equals(ip.user_ns['output'].read(), 'hi\n') | |||
|
654 | nt.assert_equals(ip.user_ns['error'].read(), 'hello\n') | |||
|
655 | ||||
|
656 | def test_script_defaults(): | |||
|
657 | ip = get_ipython() | |||
|
658 | for cmd in ['sh', 'bash', 'perl', 'ruby']: | |||
|
659 | try: | |||
|
660 | find_cmd(cmd) | |||
|
661 | except Exception: | |||
|
662 | pass | |||
|
663 | else: | |||
|
664 | nt.assert_in(cmd, ip.magics_manager.magics['cell']) |
General Comments 0
You need to be logged in to leave comments.
Login now