##// END OF EJS Templates
Add --proc option to %%script...
Takafumi Arakaki -
Show More
@@ -1,216 +1,224 b''
1 1 """Magic functions for running cells in various scripts."""
2 2 #-----------------------------------------------------------------------------
3 3 # Copyright (c) 2012 The IPython Development Team.
4 4 #
5 5 # Distributed under the terms of the Modified BSD License.
6 6 #
7 7 # The full license is in the file COPYING.txt, distributed with this software.
8 8 #-----------------------------------------------------------------------------
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Imports
12 12 #-----------------------------------------------------------------------------
13 13
14 14 # Stdlib
15 15 import os
16 16 import re
17 17 import sys
18 18 from subprocess import Popen, PIPE
19 19
20 20 # Our own packages
21 21 from IPython.config.configurable import Configurable
22 22 from IPython.core import magic_arguments
23 23 from IPython.core.error import UsageError
24 24 from IPython.core.magic import (
25 25 Magics, magics_class, line_magic, cell_magic
26 26 )
27 27 from IPython.lib.backgroundjobs import BackgroundJobManager
28 28 from IPython.testing.skipdoctest import skip_doctest
29 29 from IPython.utils import py3compat
30 30 from IPython.utils.process import find_cmd, FindCmdError, arg_split
31 31 from IPython.utils.traitlets import List, Dict
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Magic implementation classes
35 35 #-----------------------------------------------------------------------------
36 36
37 37 def script_args(f):
38 38 """single decorator for adding script args"""
39 39 args = [
40 40 magic_arguments.argument(
41 41 '--out', type=str,
42 42 help="""The variable in which to store stdout from the script.
43 43 If the script is backgrounded, this will be the stdout *pipe*,
44 44 instead of the stderr text itself.
45 45 """
46 46 ),
47 47 magic_arguments.argument(
48 48 '--err', type=str,
49 49 help="""The variable in which to store stderr from the script.
50 50 If the script is backgrounded, this will be the stderr *pipe*,
51 51 instead of the stderr text itself.
52 52 """
53 53 ),
54 54 magic_arguments.argument(
55 55 '--bg', action="store_true",
56 56 help="""Whether to run the script in the background.
57 57 If given, the only way to see the output of the command is
58 58 with --out/err.
59 59 """
60 60 ),
61 magic_arguments.argument(
62 '--proc', type=str,
63 help="""The variable in which to store Popen instance.
64 This is used only when --bg option is given.
65 """
66 ),
61 67 ]
62 68 for arg in args:
63 69 f = arg(f)
64 70 return f
65 71
66 72 @magics_class
67 73 class ScriptMagics(Magics, Configurable):
68 74 """Magics for talking to scripts
69 75
70 76 This defines a base `%%script` cell magic for running a cell
71 77 with a program in a subprocess, and registers a few top-level
72 78 magics that call %%script with common interpreters.
73 79 """
74 80 script_magics = List(config=True,
75 81 help="""Extra script cell magics to define
76 82
77 83 This generates simple wrappers of `%%script foo` as `%%foo`.
78 84
79 85 If you want to add script magics that aren't on your path,
80 86 specify them in script_paths
81 87 """,
82 88 )
83 89 def _script_magics_default(self):
84 90 """default to a common list of programs if we find them"""
85 91
86 92 defaults = []
87 93 to_try = []
88 94 if os.name == 'nt':
89 95 defaults.append('cmd')
90 96 to_try.append('powershell')
91 97 to_try.extend([
92 98 'sh',
93 99 'bash',
94 100 'perl',
95 101 'ruby',
96 102 'python3',
97 103 'pypy',
98 104 ])
99 105
100 106 for cmd in to_try:
101 107 if cmd in self.script_paths:
102 108 defaults.append(cmd)
103 109 else:
104 110 try:
105 111 find_cmd(cmd)
106 112 except FindCmdError:
107 113 # command not found, ignore it
108 114 pass
109 115 except ImportError:
110 116 # Windows without pywin32, find_cmd doesn't work
111 117 pass
112 118 else:
113 119 defaults.append(cmd)
114 120 return defaults
115 121
116 122 script_paths = Dict(config=True,
117 123 help="""Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby'
118 124
119 125 Only necessary for items in script_magics where the default path will not
120 126 find the right interpreter.
121 127 """
122 128 )
123 129
124 130 def __init__(self, shell=None):
125 131 Configurable.__init__(self, config=shell.config)
126 132 self._generate_script_magics()
127 133 Magics.__init__(self, shell=shell)
128 134 self.job_manager = BackgroundJobManager()
129 135
130 136 def _generate_script_magics(self):
131 137 cell_magics = self.magics['cell']
132 138 for name in self.script_magics:
133 139 cell_magics[name] = self._make_script_magic(name)
134 140
135 141 def _make_script_magic(self, name):
136 142 """make a named magic, that calls %%script with a particular program"""
137 143 # expand to explicit path if necessary:
138 144 script = self.script_paths.get(name, name)
139 145
140 146 @magic_arguments.magic_arguments()
141 147 @script_args
142 148 def named_script_magic(line, cell):
143 149 # if line, add it as cl-flags
144 150 if line:
145 151 line = "%s %s" % (script, line)
146 152 else:
147 153 line = script
148 154 return self.shebang(line, cell)
149 155
150 156 # write a basic docstring:
151 157 named_script_magic.__doc__ = \
152 158 """%%{name} script magic
153 159
154 160 Run cells with {script} in a subprocess.
155 161
156 162 This is a shortcut for `%%script {script}`
157 163 """.format(**locals())
158 164
159 165 return named_script_magic
160 166
161 167 @magic_arguments.magic_arguments()
162 168 @script_args
163 169 @cell_magic("script")
164 170 def shebang(self, line, cell):
165 171 """Run a cell via a shell command
166 172
167 173 The `%%script` line is like the #! line of script,
168 174 specifying a program (bash, perl, ruby, etc.) with which to run.
169 175
170 176 The rest of the cell is run by that program.
171 177
172 178 Examples
173 179 --------
174 180 ::
175 181
176 182 In [1]: %%script bash
177 183 ...: for i in 1 2 3; do
178 184 ...: echo $i
179 185 ...: done
180 186 1
181 187 2
182 188 3
183 189 """
184 190 argv = arg_split(line, posix = not sys.platform.startswith('win'))
185 191 args, cmd = self.shebang.parser.parse_known_args(argv)
186 192
187 193 p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE)
188 194
189 195 cell = cell.encode('utf8', 'replace')
190 196 if args.bg:
191 197 if args.out:
192 198 self.shell.user_ns[args.out] = p.stdout
193 199 if args.err:
194 200 self.shell.user_ns[args.err] = p.stderr
195 201 self.job_manager.new(self._run_script, p, cell)
202 if args.proc:
203 self.shell.user_ns[args.proc] = p
196 204 return
197 205
198 206 out, err = p.communicate(cell)
199 207 out = py3compat.bytes_to_str(out)
200 208 err = py3compat.bytes_to_str(err)
201 209 if args.out:
202 210 self.shell.user_ns[args.out] = out
203 211 else:
204 212 sys.stdout.write(out)
205 213 sys.stdout.flush()
206 214 if args.err:
207 215 self.shell.user_ns[args.err] = err
208 216 else:
209 217 sys.stderr.write(err)
210 218 sys.stderr.flush()
211 219
212 220 def _run_script(self, p, cell):
213 221 """callback for running the script in the background"""
214 222 p.stdin.write(cell)
215 223 p.stdin.close()
216 224 p.wait()
General Comments 0
You need to be logged in to leave comments. Login now