##// END OF EJS Templates
pyflakes to avoid missing imports
Matthias Bussonnier -
Show More
@@ -0,0 +1,5 b''
1 [flake8]
2 ignore = W293,E301,E271,E265,W291,E722,E302,C901,E225,E128,E122,E226,E231
3 max-line-length = 160
4 exclude = tests/*
5 max-complexity = 10
@@ -1,30 +1,33 b''
1 name: Run MyPy
1 name: Run MyPy
2
2
3 on:
3 on:
4 push:
4 push:
5 branches: [ master ]
5 branches: [ master ]
6 pull_request:
6 pull_request:
7 branches: [ master ]
7 branches: [ master ]
8
8
9 jobs:
9 jobs:
10 build:
10 build:
11
11
12 runs-on: ubuntu-latest
12 runs-on: ubuntu-latest
13 strategy:
13 strategy:
14 matrix:
14 matrix:
15 python-version: [3.8]
15 python-version: [3.8]
16
16
17 steps:
17 steps:
18 - uses: actions/checkout@v2
18 - uses: actions/checkout@v2
19 - name: Set up Python ${{ matrix.python-version }}
19 - name: Set up Python ${{ matrix.python-version }}
20 uses: actions/setup-python@v2
20 uses: actions/setup-python@v2
21 with:
21 with:
22 python-version: ${{ matrix.python-version }}
22 python-version: ${{ matrix.python-version }}
23 - name: Install dependencies
23 - name: Install dependencies
24 run: |
24 run: |
25 python -m pip install --upgrade pip
25 python -m pip install --upgrade pip
26 pip install mypy
26 pip install mypy pyflakes flake8
27 - name: Lint with mypy
27 - name: Lint with mypy
28 run: |
28 run: |
29 mypy IPython/terminal/ptutils.py
29 mypy IPython/terminal/ptutils.py
30 mypy IPython/core/c*.py
30 mypy IPython/core/c*.py
31 - name: Lint with pyflakes
32 run: |
33 flake8 IPython/core/magics/script.py
@@ -1,318 +1,316 b''
1 """Magic functions for running cells in various scripts."""
1 """Magic functions for running cells in various scripts."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import errno
6 import errno
7 import os
7 import os
8 import sys
8 import sys
9 import signal
9 import signal
10 import time
10 import time
11 import asyncio
11 import asyncio
12 import atexit
12 import atexit
13
13
14 from subprocess import CalledProcessError
14 from subprocess import CalledProcessError
15
15
16 from IPython.core import magic_arguments
16 from IPython.core import magic_arguments
17 from IPython.core.magic import (
17 from IPython.core.magic import (
18 Magics, magics_class, line_magic, cell_magic
18 Magics, magics_class, line_magic, cell_magic
19 )
19 )
20 from IPython.lib.backgroundjobs import BackgroundJobManager
20 from IPython.lib.backgroundjobs import BackgroundJobManager
21 from IPython.utils import py3compat
22 from IPython.utils.process import arg_split
21 from IPython.utils.process import arg_split
23 from traitlets import List, Dict, default
22 from traitlets import List, Dict, default
24
23
25
24
26 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
27 # Magic implementation classes
26 # Magic implementation classes
28 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
29
28
30 def script_args(f):
29 def script_args(f):
31 """single decorator for adding script args"""
30 """single decorator for adding script args"""
32 args = [
31 args = [
33 magic_arguments.argument(
32 magic_arguments.argument(
34 '--out', type=str,
33 '--out', type=str,
35 help="""The variable in which to store stdout from the script.
34 help="""The variable in which to store stdout from the script.
36 If the script is backgrounded, this will be the stdout *pipe*,
35 If the script is backgrounded, this will be the stdout *pipe*,
37 instead of the stderr text itself and will not be auto closed.
36 instead of the stderr text itself and will not be auto closed.
38 """
37 """
39 ),
38 ),
40 magic_arguments.argument(
39 magic_arguments.argument(
41 '--err', type=str,
40 '--err', type=str,
42 help="""The variable in which to store stderr from the script.
41 help="""The variable in which to store stderr from the script.
43 If the script is backgrounded, this will be the stderr *pipe*,
42 If the script is backgrounded, this will be the stderr *pipe*,
44 instead of the stderr text itself and will not be autoclosed.
43 instead of the stderr text itself and will not be autoclosed.
45 """
44 """
46 ),
45 ),
47 magic_arguments.argument(
46 magic_arguments.argument(
48 '--bg', action="store_true",
47 '--bg', action="store_true",
49 help="""Whether to run the script in the background.
48 help="""Whether to run the script in the background.
50 If given, the only way to see the output of the command is
49 If given, the only way to see the output of the command is
51 with --out/err.
50 with --out/err.
52 """
51 """
53 ),
52 ),
54 magic_arguments.argument(
53 magic_arguments.argument(
55 '--proc', type=str,
54 '--proc', type=str,
56 help="""The variable in which to store Popen instance.
55 help="""The variable in which to store Popen instance.
57 This is used only when --bg option is given.
56 This is used only when --bg option is given.
58 """
57 """
59 ),
58 ),
60 magic_arguments.argument(
59 magic_arguments.argument(
61 '--no-raise-error', action="store_false", dest='raise_error',
60 '--no-raise-error', action="store_false", dest='raise_error',
62 help="""Whether you should raise an error message in addition to
61 help="""Whether you should raise an error message in addition to
63 a stream on stderr if you get a nonzero exit code.
62 a stream on stderr if you get a nonzero exit code.
64 """
63 """
65 )
64 )
66 ]
65 ]
67 for arg in args:
66 for arg in args:
68 f = arg(f)
67 f = arg(f)
69 return f
68 return f
70
69
71 @magics_class
70 @magics_class
72 class ScriptMagics(Magics):
71 class ScriptMagics(Magics):
73 """Magics for talking to scripts
72 """Magics for talking to scripts
74
73
75 This defines a base `%%script` cell magic for running a cell
74 This defines a base `%%script` cell magic for running a cell
76 with a program in a subprocess, and registers a few top-level
75 with a program in a subprocess, and registers a few top-level
77 magics that call %%script with common interpreters.
76 magics that call %%script with common interpreters.
78 """
77 """
79 script_magics = List(
78 script_magics = List(
80 help="""Extra script cell magics to define
79 help="""Extra script cell magics to define
81
80
82 This generates simple wrappers of `%%script foo` as `%%foo`.
81 This generates simple wrappers of `%%script foo` as `%%foo`.
83
82
84 If you want to add script magics that aren't on your path,
83 If you want to add script magics that aren't on your path,
85 specify them in script_paths
84 specify them in script_paths
86 """,
85 """,
87 ).tag(config=True)
86 ).tag(config=True)
88 @default('script_magics')
87 @default('script_magics')
89 def _script_magics_default(self):
88 def _script_magics_default(self):
90 """default to a common list of programs"""
89 """default to a common list of programs"""
91
90
92 defaults = [
91 defaults = [
93 'sh',
92 'sh',
94 'bash',
93 'bash',
95 'perl',
94 'perl',
96 'ruby',
95 'ruby',
97 'python',
96 'python',
98 'python2',
97 'python2',
99 'python3',
98 'python3',
100 'pypy',
99 'pypy',
101 ]
100 ]
102 if os.name == 'nt':
101 if os.name == 'nt':
103 defaults.extend([
102 defaults.extend([
104 'cmd',
103 'cmd',
105 ])
104 ])
106
105
107 return defaults
106 return defaults
108
107
109 script_paths = Dict(
108 script_paths = Dict(
110 help="""Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby'
109 help="""Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby'
111
110
112 Only necessary for items in script_magics where the default path will not
111 Only necessary for items in script_magics where the default path will not
113 find the right interpreter.
112 find the right interpreter.
114 """
113 """
115 ).tag(config=True)
114 ).tag(config=True)
116
115
117 def __init__(self, shell=None):
116 def __init__(self, shell=None):
118 super(ScriptMagics, self).__init__(shell=shell)
117 super(ScriptMagics, self).__init__(shell=shell)
119 self._generate_script_magics()
118 self._generate_script_magics()
120 self.job_manager = BackgroundJobManager()
119 self.job_manager = BackgroundJobManager()
121 self.bg_processes = []
120 self.bg_processes = []
122 atexit.register(self.kill_bg_processes)
121 atexit.register(self.kill_bg_processes)
123
122
124 def __del__(self):
123 def __del__(self):
125 self.kill_bg_processes()
124 self.kill_bg_processes()
126
125
127 def _generate_script_magics(self):
126 def _generate_script_magics(self):
128 cell_magics = self.magics['cell']
127 cell_magics = self.magics['cell']
129 for name in self.script_magics:
128 for name in self.script_magics:
130 cell_magics[name] = self._make_script_magic(name)
129 cell_magics[name] = self._make_script_magic(name)
131
130
132 def _make_script_magic(self, name):
131 def _make_script_magic(self, name):
133 """make a named magic, that calls %%script with a particular program"""
132 """make a named magic, that calls %%script with a particular program"""
134 # expand to explicit path if necessary:
133 # expand to explicit path if necessary:
135 script = self.script_paths.get(name, name)
134 script = self.script_paths.get(name, name)
136
135
137 @magic_arguments.magic_arguments()
136 @magic_arguments.magic_arguments()
138 @script_args
137 @script_args
139 def named_script_magic(line, cell):
138 def named_script_magic(line, cell):
140 # if line, add it as cl-flags
139 # if line, add it as cl-flags
141 if line:
140 if line:
142 line = "%s %s" % (script, line)
141 line = "%s %s" % (script, line)
143 else:
142 else:
144 line = script
143 line = script
145 return self.shebang(line, cell)
144 return self.shebang(line, cell)
146
145
147 # write a basic docstring:
146 # write a basic docstring:
148 named_script_magic.__doc__ = \
147 named_script_magic.__doc__ = \
149 """%%{name} script magic
148 """%%{name} script magic
150
149
151 Run cells with {script} in a subprocess.
150 Run cells with {script} in a subprocess.
152
151
153 This is a shortcut for `%%script {script}`
152 This is a shortcut for `%%script {script}`
154 """.format(**locals())
153 """.format(**locals())
155
154
156 return named_script_magic
155 return named_script_magic
157
156
158 @magic_arguments.magic_arguments()
157 @magic_arguments.magic_arguments()
159 @script_args
158 @script_args
160 @cell_magic("script")
159 @cell_magic("script")
161 def shebang(self, line, cell):
160 def shebang(self, line, cell):
162 """Run a cell via a shell command
161 """Run a cell via a shell command
163
162
164 The `%%script` line is like the #! line of script,
163 The `%%script` line is like the #! line of script,
165 specifying a program (bash, perl, ruby, etc.) with which to run.
164 specifying a program (bash, perl, ruby, etc.) with which to run.
166
165
167 The rest of the cell is run by that program.
166 The rest of the cell is run by that program.
168
167
169 Examples
168 Examples
170 --------
169 --------
171 ::
170 ::
172
171
173 In [1]: %%script bash
172 In [1]: %%script bash
174 ...: for i in 1 2 3; do
173 ...: for i in 1 2 3; do
175 ...: echo $i
174 ...: echo $i
176 ...: done
175 ...: done
177 1
176 1
178 2
177 2
179 3
178 3
180 """
179 """
181
180
182 async def _handle_stream(stream, stream_arg, file_object):
181 async def _handle_stream(stream, stream_arg, file_object):
183 while True:
182 while True:
184 line = (await stream.readline()).decode("utf8")
183 line = (await stream.readline()).decode("utf8")
185 if not line:
184 if not line:
186 break
185 break
187 if stream_arg:
186 if stream_arg:
188 self.shell.user_ns[stream_arg] = line
187 self.shell.user_ns[stream_arg] = line
189 else:
188 else:
190 file_object.write(line)
189 file_object.write(line)
191 file_object.flush()
190 file_object.flush()
192
191
193 async def _stream_communicate(process, cell):
192 async def _stream_communicate(process, cell):
194 process.stdin.write(cell)
193 process.stdin.write(cell)
195 process.stdin.close()
194 process.stdin.close()
196 stdout_task = asyncio.create_task(
195 stdout_task = asyncio.create_task(
197 _handle_stream(process.stdout, args.out, sys.stdout)
196 _handle_stream(process.stdout, args.out, sys.stdout)
198 )
197 )
199 stderr_task = asyncio.create_task(
198 stderr_task = asyncio.create_task(
200 _handle_stream(process.stderr, args.err, sys.stderr)
199 _handle_stream(process.stderr, args.err, sys.stderr)
201 )
200 )
202 await asyncio.wait([stdout_task, stderr_task])
201 await asyncio.wait([stdout_task, stderr_task])
203
202
204 if sys.platform.startswith("win"):
203 if sys.platform.startswith("win"):
205 asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
204 asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
206 loop = asyncio.get_event_loop()
205 loop = asyncio.get_event_loop()
207
206
208 argv = arg_split(line, posix=not sys.platform.startswith("win"))
207 argv = arg_split(line, posix=not sys.platform.startswith("win"))
209 args, cmd = self.shebang.parser.parse_known_args(argv)
208 args, cmd = self.shebang.parser.parse_known_args(argv)
210 try:
209 try:
211 p = loop.run_until_complete(
210 p = loop.run_until_complete(
212 asyncio.create_subprocess_exec(
211 asyncio.create_subprocess_exec(
213 *cmd,
212 *cmd,
214 stdout=asyncio.subprocess.PIPE,
213 stdout=asyncio.subprocess.PIPE,
215 stderr=asyncio.subprocess.PIPE,
214 stderr=asyncio.subprocess.PIPE,
216 stdin=asyncio.subprocess.PIPE
215 stdin=asyncio.subprocess.PIPE
217 )
216 )
218 )
217 )
219 except OSError as e:
218 except OSError as e:
220 if e.errno == errno.ENOENT:
219 if e.errno == errno.ENOENT:
221 print("Couldn't find program: %r" % cmd[0])
220 print("Couldn't find program: %r" % cmd[0])
222 return
221 return
223 else:
222 else:
224 raise
223 raise
225
224
226 if not cell.endswith('\n'):
225 if not cell.endswith('\n'):
227 cell += '\n'
226 cell += '\n'
228 cell = cell.encode('utf8', 'replace')
227 cell = cell.encode('utf8', 'replace')
229 if args.bg:
228 if args.bg:
230 self.bg_processes.append(p)
229 self.bg_processes.append(p)
231 self._gc_bg_processes()
230 self._gc_bg_processes()
232 to_close = []
231 to_close = []
233 if args.out:
232 if args.out:
234 self.shell.user_ns[args.out] = p.stdout
233 self.shell.user_ns[args.out] = p.stdout
235 else:
234 else:
236 to_close.append(p.stdout)
235 to_close.append(p.stdout)
237 if args.err:
236 if args.err:
238 self.shell.user_ns[args.err] = p.stderr
237 self.shell.user_ns[args.err] = p.stderr
239 else:
238 else:
240 to_close.append(p.stderr)
239 to_close.append(p.stderr)
241 self.job_manager.new(self._run_script, p, cell, to_close, daemon=True)
240 self.job_manager.new(self._run_script, p, cell, to_close, daemon=True)
242 if args.proc:
241 if args.proc:
243 self.shell.user_ns[args.proc] = p
242 self.shell.user_ns[args.proc] = p
244 return
243 return
245
244
246 try:
245 try:
247 loop.run_until_complete(_stream_communicate(p, cell))
246 loop.run_until_complete(_stream_communicate(p, cell))
248 except KeyboardInterrupt:
247 except KeyboardInterrupt:
249 try:
248 try:
250 p.send_signal(signal.SIGINT)
249 p.send_signal(signal.SIGINT)
251 time.sleep(0.1)
250 time.sleep(0.1)
252 if p.returncode is not None:
251 if p.returncode is not None:
253 print("Process is interrupted.")
252 print("Process is interrupted.")
254 return
253 return
255 p.terminate()
254 p.terminate()
256 time.sleep(0.1)
255 time.sleep(0.1)
257 if p.returncode is not None:
256 if p.returncode is not None:
258 print("Process is terminated.")
257 print("Process is terminated.")
259 return
258 return
260 p.kill()
259 p.kill()
261 print("Process is killed.")
260 print("Process is killed.")
262 except OSError:
261 except OSError:
263 pass
262 pass
264 except Exception as e:
263 except Exception as e:
265 print("Error while terminating subprocess (pid=%i): %s" \
264 print("Error while terminating subprocess (pid=%i): %s" % (p.pid, e))
266 % (p.pid, e))
267 return
265 return
268 if args.raise_error and p.returncode!=0:
266 if args.raise_error and p.returncode!=0:
269 raise CalledProcessError(p.returncode, cell)
267 raise CalledProcessError(p.returncode, cell)
270
268
271 def _run_script(self, p, cell, to_close):
269 def _run_script(self, p, cell, to_close):
272 """callback for running the script in the background"""
270 """callback for running the script in the background"""
273 p.stdin.write(cell)
271 p.stdin.write(cell)
274 p.stdin.close()
272 p.stdin.close()
275 for s in to_close:
273 for s in to_close:
276 s.close()
274 s.close()
277 p.wait()
275 p.wait()
278
276
279 @line_magic("killbgscripts")
277 @line_magic("killbgscripts")
280 def killbgscripts(self, _nouse_=''):
278 def killbgscripts(self, _nouse_=''):
281 """Kill all BG processes started by %%script and its family."""
279 """Kill all BG processes started by %%script and its family."""
282 self.kill_bg_processes()
280 self.kill_bg_processes()
283 print("All background processes were killed.")
281 print("All background processes were killed.")
284
282
285 def kill_bg_processes(self):
283 def kill_bg_processes(self):
286 """Kill all BG processes which are still running."""
284 """Kill all BG processes which are still running."""
287 if not self.bg_processes:
285 if not self.bg_processes:
288 return
286 return
289 for p in self.bg_processes:
287 for p in self.bg_processes:
290 if p.returncode is None:
288 if p.returncode is None:
291 try:
289 try:
292 p.send_signal(signal.SIGINT)
290 p.send_signal(signal.SIGINT)
293 except:
291 except:
294 pass
292 pass
295 time.sleep(0.1)
293 time.sleep(0.1)
296 self._gc_bg_processes()
294 self._gc_bg_processes()
297 if not self.bg_processes:
295 if not self.bg_processes:
298 return
296 return
299 for p in self.bg_processes:
297 for p in self.bg_processes:
300 if p.returncode is None:
298 if p.returncode is None:
301 try:
299 try:
302 p.terminate()
300 p.terminate()
303 except:
301 except:
304 pass
302 pass
305 time.sleep(0.1)
303 time.sleep(0.1)
306 self._gc_bg_processes()
304 self._gc_bg_processes()
307 if not self.bg_processes:
305 if not self.bg_processes:
308 return
306 return
309 for p in self.bg_processes:
307 for p in self.bg_processes:
310 if p.returncode is None:
308 if p.returncode is None:
311 try:
309 try:
312 p.kill()
310 p.kill()
313 except:
311 except:
314 pass
312 pass
315 self._gc_bg_processes()
313 self._gc_bg_processes()
316
314
317 def _gc_bg_processes(self):
315 def _gc_bg_processes(self):
318 self.bg_processes = [p for p in self.bg_processes if p.returncode is None]
316 self.bg_processes = [p for p in self.bg_processes if p.returncode is None]
@@ -1,46 +1,47 b''
1 include README.rst
1 include README.rst
2 include COPYING.rst
2 include COPYING.rst
3 include LICENSE
3 include LICENSE
4 include setupbase.py
4 include setupbase.py
5 include setupegg.py
5 include setupegg.py
6 include MANIFEST.in
6 include MANIFEST.in
7 include pytest.ini
7 include pytest.ini
8 include mypy.ini
8 include mypy.ini
9 include .mailmap
9 include .mailmap
10 include .flake8
10
11
11 recursive-exclude tools *
12 recursive-exclude tools *
12 exclude tools
13 exclude tools
13 exclude CONTRIBUTING.md
14 exclude CONTRIBUTING.md
14 exclude .editorconfig
15 exclude .editorconfig
15
16
16 graft setupext
17 graft setupext
17
18
18 graft scripts
19 graft scripts
19
20
20 # Load main dir but exclude things we don't want in the distro
21 # Load main dir but exclude things we don't want in the distro
21 graft IPython
22 graft IPython
22
23
23 # Documentation
24 # Documentation
24 graft docs
25 graft docs
25 exclude docs/\#*
26 exclude docs/\#*
26 exclude docs/man/*.1.gz
27 exclude docs/man/*.1.gz
27
28
28 exclude .git-blame-ignore-revs
29 exclude .git-blame-ignore-revs
29
30
30 # Examples
31 # Examples
31 graft examples
32 graft examples
32
33
33 # docs subdirs we want to skip
34 # docs subdirs we want to skip
34 prune docs/build
35 prune docs/build
35 prune docs/gh-pages
36 prune docs/gh-pages
36 prune docs/dist
37 prune docs/dist
37
38
38 # Patterns to exclude from any directory
39 # Patterns to exclude from any directory
39 global-exclude *~
40 global-exclude *~
40 global-exclude *.flc
41 global-exclude *.flc
41 global-exclude *.yml
42 global-exclude *.yml
42 global-exclude *.pyc
43 global-exclude *.pyc
43 global-exclude *.pyo
44 global-exclude *.pyo
44 global-exclude .dircopy.log
45 global-exclude .dircopy.log
45 global-exclude .git
46 global-exclude .git
46 global-exclude .ipynb_checkpoints
47 global-exclude .ipynb_checkpoints
General Comments 0
You need to be logged in to leave comments. Login now