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 | 2017 | self.register_magics(m.AutoMagics, m.BasicMagics, m.CodeMagics, |
|
2018 | 2018 | m.ConfigMagics, m.DeprecatedMagics, m.ExecutionMagics, |
|
2019 | 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 | 2023 | # FIXME: Move the color initialization to the DisplayHook, which |
|
2023 | 2024 | # should be split into a prompt manager and displayhook. We probably |
@@ -30,7 +30,7 b' from IPython.external.decorator import decorator' | |||
|
30 | 30 | from IPython.utils.ipstruct import Struct |
|
31 | 31 | from IPython.utils.process import arg_split |
|
32 | 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 | 34 | from IPython.utils.warn import error, warn |
|
35 | 35 | |
|
36 | 36 | #----------------------------------------------------------------------------- |
@@ -365,10 +365,10 b' class MagicsManager(Configurable):' | |||
|
365 | 365 | for m in magic_objects: |
|
366 | 366 | if not m.registered: |
|
367 | 367 | raise ValueError("Class of magics %r was constructed without " |
|
368 |
"the @register_ma |
|
|
369 |
if type(m) i |
|
|
368 | "the @register_magics class decorator") | |
|
369 | if type(m) in (type, MetaHasTraits): | |
|
370 | 370 | # If we're given an uninstantiated class |
|
371 | m = m(self.shell) | |
|
371 | m = m(shell=self.shell) | |
|
372 | 372 | |
|
373 | 373 | # Now that we have an instance, we can register it and update the |
|
374 | 374 | # table of callables |
@@ -25,6 +25,7 b' from .logging import LoggingMagics' | |||
|
25 | 25 | from .namespace import NamespaceMagics |
|
26 | 26 | from .osm import OSMagics |
|
27 | 27 | from .pylab import PylabMagics |
|
28 | from .script import ScriptMagics | |
|
28 | 29 | |
|
29 | 30 | #----------------------------------------------------------------------------- |
|
30 | 31 | # Magic implementation classes |
@@ -16,17 +16,20 b' builtin.' | |||
|
16 | 16 | #----------------------------------------------------------------------------- |
|
17 | 17 | |
|
18 | 18 | # Stdlib |
|
19 | import io | |
|
19 | 20 | import os |
|
20 | 21 | import re |
|
21 | 22 | import sys |
|
22 | 23 | from pprint import pformat |
|
23 | 24 | |
|
24 | 25 | # Our own packages |
|
26 | from IPython.core import magic_arguments | |
|
25 | 27 | from IPython.core import oinspect |
|
26 | 28 | from IPython.core import page |
|
27 | from IPython.core.error import UsageError | |
|
28 |
from IPython.core.magic import ( |
|
|
29 | line_magic) | |
|
29 | from IPython.core.error import UsageError, StdinNotImplementedError | |
|
30 | from IPython.core.magic import ( | |
|
31 | Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic | |
|
32 | ) | |
|
30 | 33 | from IPython.testing.skipdoctest import skip_doctest |
|
31 | 34 | from IPython.utils.io import file_read, nlprint |
|
32 | 35 | from IPython.utils.path import get_py_filename, unquote_filename |
@@ -432,7 +435,7 b' class OSMagics(Magics):' | |||
|
432 | 435 | @skip_doctest |
|
433 | 436 | @line_magic |
|
434 | 437 | def sc(self, parameter_s=''): |
|
435 |
"""Shell capture - |
|
|
438 | """Shell capture - run shell command and capture output (DEPRECATED use !). | |
|
436 | 439 | |
|
437 | 440 | DEPRECATED. Suboptimal, retained for backwards compatibility. |
|
438 | 441 | |
@@ -545,9 +548,9 b' class OSMagics(Magics):' | |||
|
545 | 548 | else: |
|
546 | 549 | return out |
|
547 | 550 | |
|
548 | @line_magic | |
|
549 |
def sx(self, |
|
|
550 |
"""Shell execute - run |
|
|
551 | @line_cell_magic | |
|
552 | def sx(self, line='', cell=None): | |
|
553 | """Shell execute - run shell command and capture output (!! is short-hand). | |
|
551 | 554 | |
|
552 | 555 | %sx command |
|
553 | 556 | |
@@ -586,10 +589,21 b' class OSMagics(Magics):' | |||
|
586 | 589 | |
|
587 | 590 | This is very useful when trying to use such lists as arguments to |
|
588 | 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: | |
|
591 | return self.shell.getoutput(parameter_s) | |
|
592 | ||
|
605 | system = line_cell_magic('system')(sx) | |
|
606 | bang = cell_magic('!')(sx) | |
|
593 | 607 | |
|
594 | 608 | @line_magic |
|
595 | 609 | def bookmark(self, parameter_s=''): |
@@ -675,3 +689,33 b' class OSMagics(Magics):' | |||
|
675 | 689 | return |
|
676 | 690 | |
|
677 | 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 | 28 | cell_magic, line_cell_magic, |
|
29 | 29 | register_line_magic, register_cell_magic, |
|
30 | 30 | register_line_cell_magic) |
|
31 | from IPython.core.magics import execution | |
|
31 | from IPython.core.magics import execution, script | |
|
32 | 32 | from IPython.nbformat.v3.tests.nbexamples import nb0 |
|
33 | 33 | from IPython.nbformat import current |
|
34 | 34 | from IPython.testing import decorators as dec |
|
35 | 35 | from IPython.testing import tools as tt |
|
36 | 36 | from IPython.utils import py3compat |
|
37 | 37 | from IPython.utils.tempdir import TemporaryDirectory |
|
38 | from IPython.utils.process import find_cmd | |
|
38 | 39 | |
|
39 | 40 | #----------------------------------------------------------------------------- |
|
40 | 41 | # Test functions begin |
@@ -560,4 +561,104 b' class CellMagicTestCase(TestCase):' | |||
|
560 | 561 | # Check that nothing is registered as 'cellm33' |
|
561 | 562 | c33 = _ip.find_cell_magic('cellm33') |
|
562 | 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