##// END OF EJS Templates
Fixed lint issue
Paul Bissex -
Show More
@@ -1,733 +1,737 b''
1 """Implementation of code management magic functions.
1 """Implementation of code management magic functions.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2012 The IPython Development Team.
4 # Copyright (c) 2012 The IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 # Stdlib
15 # Stdlib
16 import inspect
16 import inspect
17 import io
17 import io
18 import os
18 import os
19 import re
19 import re
20 import sys
20 import sys
21 import ast
21 import ast
22 from itertools import chain
22 from itertools import chain
23 from urllib.request import Request, urlopen
23 from urllib.request import Request, urlopen
24 from urllib.parse import urlencode
24 from urllib.parse import urlencode
25 from pathlib import Path
25 from pathlib import Path
26
26
27 # Our own packages
27 # Our own packages
28 from IPython.core.error import TryNext, StdinNotImplementedError, UsageError
28 from IPython.core.error import TryNext, StdinNotImplementedError, UsageError
29 from IPython.core.macro import Macro
29 from IPython.core.macro import Macro
30 from IPython.core.magic import Magics, magics_class, line_magic
30 from IPython.core.magic import Magics, magics_class, line_magic
31 from IPython.core.oinspect import find_file, find_source_lines
31 from IPython.core.oinspect import find_file, find_source_lines
32 from IPython.core.release import version
32 from IPython.core.release import version
33 from IPython.testing.skipdoctest import skip_doctest
33 from IPython.testing.skipdoctest import skip_doctest
34 from IPython.utils.contexts import preserve_keys
34 from IPython.utils.contexts import preserve_keys
35 from IPython.utils.path import get_py_filename
35 from IPython.utils.path import get_py_filename
36 from warnings import warn
36 from warnings import warn
37 from logging import error
37 from logging import error
38 from IPython.utils.text import get_text_list
38 from IPython.utils.text import get_text_list
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Magic implementation classes
41 # Magic implementation classes
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43
43
44 # Used for exception handling in magic_edit
44 # Used for exception handling in magic_edit
45 class MacroToEdit(ValueError): pass
45 class MacroToEdit(ValueError): pass
46
46
47 ipython_input_pat = re.compile(r"<ipython\-input\-(\d+)-[a-z\d]+>$")
47 ipython_input_pat = re.compile(r"<ipython\-input\-(\d+)-[a-z\d]+>$")
48
48
49 # To match, e.g. 8-10 1:5 :10 3-
49 # To match, e.g. 8-10 1:5 :10 3-
50 range_re = re.compile(r"""
50 range_re = re.compile(r"""
51 (?P<start>\d+)?
51 (?P<start>\d+)?
52 ((?P<sep>[\-:])
52 ((?P<sep>[\-:])
53 (?P<end>\d+)?)?
53 (?P<end>\d+)?)?
54 $""", re.VERBOSE)
54 $""", re.VERBOSE)
55
55
56
56
57 def extract_code_ranges(ranges_str):
57 def extract_code_ranges(ranges_str):
58 """Turn a string of range for %%load into 2-tuples of (start, stop)
58 """Turn a string of range for %%load into 2-tuples of (start, stop)
59 ready to use as a slice of the content split by lines.
59 ready to use as a slice of the content split by lines.
60
60
61 Examples
61 Examples
62 --------
62 --------
63 list(extract_input_ranges("5-10 2"))
63 list(extract_input_ranges("5-10 2"))
64 [(4, 10), (1, 2)]
64 [(4, 10), (1, 2)]
65 """
65 """
66 for range_str in ranges_str.split():
66 for range_str in ranges_str.split():
67 rmatch = range_re.match(range_str)
67 rmatch = range_re.match(range_str)
68 if not rmatch:
68 if not rmatch:
69 continue
69 continue
70 sep = rmatch.group("sep")
70 sep = rmatch.group("sep")
71 start = rmatch.group("start")
71 start = rmatch.group("start")
72 end = rmatch.group("end")
72 end = rmatch.group("end")
73
73
74 if sep == '-':
74 if sep == '-':
75 start = int(start) - 1 if start else None
75 start = int(start) - 1 if start else None
76 end = int(end) if end else None
76 end = int(end) if end else None
77 elif sep == ':':
77 elif sep == ':':
78 start = int(start) - 1 if start else None
78 start = int(start) - 1 if start else None
79 end = int(end) - 1 if end else None
79 end = int(end) - 1 if end else None
80 else:
80 else:
81 end = int(start)
81 end = int(start)
82 start = int(start) - 1
82 start = int(start) - 1
83 yield (start, end)
83 yield (start, end)
84
84
85
85
86 def extract_symbols(code, symbols):
86 def extract_symbols(code, symbols):
87 """
87 """
88 Return a tuple (blocks, not_found)
88 Return a tuple (blocks, not_found)
89 where ``blocks`` is a list of code fragments
89 where ``blocks`` is a list of code fragments
90 for each symbol parsed from code, and ``not_found`` are
90 for each symbol parsed from code, and ``not_found`` are
91 symbols not found in the code.
91 symbols not found in the code.
92
92
93 For example::
93 For example::
94
94
95 In [1]: code = '''a = 10
95 In [1]: code = '''a = 10
96 ...: def b(): return 42
96 ...: def b(): return 42
97 ...: class A: pass'''
97 ...: class A: pass'''
98
98
99 In [2]: extract_symbols(code, 'A,b,z')
99 In [2]: extract_symbols(code, 'A,b,z')
100 Out[2]: (['class A: pass\\n', 'def b(): return 42\\n'], ['z'])
100 Out[2]: (['class A: pass\\n', 'def b(): return 42\\n'], ['z'])
101 """
101 """
102 symbols = symbols.split(',')
102 symbols = symbols.split(',')
103
103
104 # this will raise SyntaxError if code isn't valid Python
104 # this will raise SyntaxError if code isn't valid Python
105 py_code = ast.parse(code)
105 py_code = ast.parse(code)
106
106
107 marks = [(getattr(s, 'name', None), s.lineno) for s in py_code.body]
107 marks = [(getattr(s, 'name', None), s.lineno) for s in py_code.body]
108 code = code.split('\n')
108 code = code.split('\n')
109
109
110 symbols_lines = {}
110 symbols_lines = {}
111
111
112 # we already know the start_lineno of each symbol (marks).
112 # we already know the start_lineno of each symbol (marks).
113 # To find each end_lineno, we traverse in reverse order until each
113 # To find each end_lineno, we traverse in reverse order until each
114 # non-blank line
114 # non-blank line
115 end = len(code)
115 end = len(code)
116 for name, start in reversed(marks):
116 for name, start in reversed(marks):
117 while not code[end - 1].strip():
117 while not code[end - 1].strip():
118 end -= 1
118 end -= 1
119 if name:
119 if name:
120 symbols_lines[name] = (start - 1, end)
120 symbols_lines[name] = (start - 1, end)
121 end = start - 1
121 end = start - 1
122
122
123 # Now symbols_lines is a map
123 # Now symbols_lines is a map
124 # {'symbol_name': (start_lineno, end_lineno), ...}
124 # {'symbol_name': (start_lineno, end_lineno), ...}
125
125
126 # fill a list with chunks of codes for each requested symbol
126 # fill a list with chunks of codes for each requested symbol
127 blocks = []
127 blocks = []
128 not_found = []
128 not_found = []
129 for symbol in symbols:
129 for symbol in symbols:
130 if symbol in symbols_lines:
130 if symbol in symbols_lines:
131 start, end = symbols_lines[symbol]
131 start, end = symbols_lines[symbol]
132 blocks.append('\n'.join(code[start:end]) + '\n')
132 blocks.append('\n'.join(code[start:end]) + '\n')
133 else:
133 else:
134 not_found.append(symbol)
134 not_found.append(symbol)
135
135
136 return blocks, not_found
136 return blocks, not_found
137
137
138 def strip_initial_indent(lines):
138 def strip_initial_indent(lines):
139 """For %load, strip indent from lines until finding an unindented line.
139 """For %load, strip indent from lines until finding an unindented line.
140
140
141 https://github.com/ipython/ipython/issues/9775
141 https://github.com/ipython/ipython/issues/9775
142 """
142 """
143 indent_re = re.compile(r'\s+')
143 indent_re = re.compile(r'\s+')
144
144
145 it = iter(lines)
145 it = iter(lines)
146 first_line = next(it)
146 first_line = next(it)
147 indent_match = indent_re.match(first_line)
147 indent_match = indent_re.match(first_line)
148
148
149 if indent_match:
149 if indent_match:
150 # First line was indented
150 # First line was indented
151 indent = indent_match.group()
151 indent = indent_match.group()
152 yield first_line[len(indent):]
152 yield first_line[len(indent):]
153
153
154 for line in it:
154 for line in it:
155 if line.startswith(indent):
155 if line.startswith(indent):
156 yield line[len(indent):]
156 yield line[len(indent):]
157 else:
157 else:
158 # Less indented than the first line - stop dedenting
158 # Less indented than the first line - stop dedenting
159 yield line
159 yield line
160 break
160 break
161 else:
161 else:
162 yield first_line
162 yield first_line
163
163
164 # Pass the remaining lines through without dedenting
164 # Pass the remaining lines through without dedenting
165 for line in it:
165 for line in it:
166 yield line
166 yield line
167
167
168
168
169 class InteractivelyDefined(Exception):
169 class InteractivelyDefined(Exception):
170 """Exception for interactively defined variable in magic_edit"""
170 """Exception for interactively defined variable in magic_edit"""
171 def __init__(self, index):
171 def __init__(self, index):
172 self.index = index
172 self.index = index
173
173
174
174
175 @magics_class
175 @magics_class
176 class CodeMagics(Magics):
176 class CodeMagics(Magics):
177 """Magics related to code management (loading, saving, editing, ...)."""
177 """Magics related to code management (loading, saving, editing, ...)."""
178
178
179 def __init__(self, *args, **kwargs):
179 def __init__(self, *args, **kwargs):
180 self._knowntemps = set()
180 self._knowntemps = set()
181 super(CodeMagics, self).__init__(*args, **kwargs)
181 super(CodeMagics, self).__init__(*args, **kwargs)
182
182
183 @line_magic
183 @line_magic
184 def save(self, parameter_s=''):
184 def save(self, parameter_s=''):
185 """Save a set of lines or a macro to a given filename.
185 """Save a set of lines or a macro to a given filename.
186
186
187 Usage:\\
187 Usage:\\
188 %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ...
188 %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ...
189
189
190 Options:
190 Options:
191
191
192 -r: use 'raw' input. By default, the 'processed' history is used,
192 -r: use 'raw' input. By default, the 'processed' history is used,
193 so that magics are loaded in their transformed version to valid
193 so that magics are loaded in their transformed version to valid
194 Python. If this option is given, the raw input as typed as the
194 Python. If this option is given, the raw input as typed as the
195 command line is used instead.
195 command line is used instead.
196
196
197 -f: force overwrite. If file exists, %save will prompt for overwrite
197 -f: force overwrite. If file exists, %save will prompt for overwrite
198 unless -f is given.
198 unless -f is given.
199
199
200 -a: append to the file instead of overwriting it.
200 -a: append to the file instead of overwriting it.
201
201
202 This function uses the same syntax as %history for input ranges,
202 This function uses the same syntax as %history for input ranges,
203 then saves the lines to the filename you specify.
203 then saves the lines to the filename you specify.
204
204
205 It adds a '.py' extension to the file if you don't do so yourself, and
205 It adds a '.py' extension to the file if you don't do so yourself, and
206 it asks for confirmation before overwriting existing files.
206 it asks for confirmation before overwriting existing files.
207
207
208 If `-r` option is used, the default extension is `.ipy`.
208 If `-r` option is used, the default extension is `.ipy`.
209 """
209 """
210
210
211 opts,args = self.parse_options(parameter_s,'fra',mode='list')
211 opts,args = self.parse_options(parameter_s,'fra',mode='list')
212 if not args:
212 if not args:
213 raise UsageError('Missing filename.')
213 raise UsageError('Missing filename.')
214 raw = 'r' in opts
214 raw = 'r' in opts
215 force = 'f' in opts
215 force = 'f' in opts
216 append = 'a' in opts
216 append = 'a' in opts
217 mode = 'a' if append else 'w'
217 mode = 'a' if append else 'w'
218 ext = '.ipy' if raw else '.py'
218 ext = '.ipy' if raw else '.py'
219 fname, codefrom = args[0], " ".join(args[1:])
219 fname, codefrom = args[0], " ".join(args[1:])
220 if not fname.endswith(('.py','.ipy')):
220 if not fname.endswith(('.py','.ipy')):
221 fname += ext
221 fname += ext
222 file_exists = os.path.isfile(fname)
222 file_exists = os.path.isfile(fname)
223 if file_exists and not force and not append:
223 if file_exists and not force and not append:
224 try:
224 try:
225 overwrite = self.shell.ask_yes_no('File `%s` exists. Overwrite (y/[N])? ' % fname, default='n')
225 overwrite = self.shell.ask_yes_no('File `%s` exists. Overwrite (y/[N])? ' % fname, default='n')
226 except StdinNotImplementedError:
226 except StdinNotImplementedError:
227 print("File `%s` exists. Use `%%save -f %s` to force overwrite" % (fname, parameter_s))
227 print("File `%s` exists. Use `%%save -f %s` to force overwrite" % (fname, parameter_s))
228 return
228 return
229 if not overwrite :
229 if not overwrite :
230 print('Operation cancelled.')
230 print('Operation cancelled.')
231 return
231 return
232 try:
232 try:
233 cmds = self.shell.find_user_code(codefrom,raw)
233 cmds = self.shell.find_user_code(codefrom,raw)
234 except (TypeError, ValueError) as e:
234 except (TypeError, ValueError) as e:
235 print(e.args[0])
235 print(e.args[0])
236 return
236 return
237 with io.open(fname, mode, encoding="utf-8") as f:
237 with io.open(fname, mode, encoding="utf-8") as f:
238 if not file_exists or not append:
238 if not file_exists or not append:
239 f.write("# coding: utf-8\n")
239 f.write("# coding: utf-8\n")
240 f.write(cmds)
240 f.write(cmds)
241 # make sure we end on a newline
241 # make sure we end on a newline
242 if not cmds.endswith('\n'):
242 if not cmds.endswith('\n'):
243 f.write('\n')
243 f.write('\n')
244 print('The following commands were written to file `%s`:' % fname)
244 print('The following commands were written to file `%s`:' % fname)
245 print(cmds)
245 print(cmds)
246
246
247 @line_magic
247 @line_magic
248 def pastebin(self, parameter_s=''):
248 def pastebin(self, parameter_s=''):
249 """Upload code to dpaste.com, returning the URL.
249 """Upload code to dpaste.com, returning the URL.
250
250
251 Usage:\\
251 Usage:\\
252 %pastebin [-d "Custom description"] 1-7
252 %pastebin [-d "Custom description"] 1-7
253
253
254 The argument can be an input history range, a filename, or the name of a
254 The argument can be an input history range, a filename, or the name of a
255 string or macro.
255 string or macro.
256
256
257 Options:
257 Options:
258
258
259 -d: Pass a custom description. The default will say
259 -d: Pass a custom description. The default will say
260 "Pasted from IPython".
260 "Pasted from IPython".
261 """
261 """
262 opts, args = self.parse_options(parameter_s, 'd:')
262 opts, args = self.parse_options(parameter_s, 'd:')
263
263
264 try:
264 try:
265 code = self.shell.find_user_code(args)
265 code = self.shell.find_user_code(args)
266 except (ValueError, TypeError) as e:
266 except (ValueError, TypeError) as e:
267 print(e.args[0])
267 print(e.args[0])
268 return
268 return
269
269
270 post_data = urlencode({
270 post_data = urlencode(
271 "title": opts.get('d', "Pasted from IPython"),
271 {
272 "syntax": "python",
272 "title": opts.get("d", "Pasted from IPython"),
273 "content": code
273 "syntax": "python",
274 }).encode('utf-8')
274 "content": code,
275
275 }
276 request = Request("http://dpaste.com/api/v2/",
276 ).encode("utf-8")
277 headers={"User-Agent": "IPython v{}".format(version)})
277
278 request = Request(
279 "http://dpaste.com/api/v2/",
280 headers={"User-Agent": "IPython v{}".format(version)},
281 )
278 response = urlopen(request, post_data)
282 response = urlopen(request, post_data)
279 return response.headers.get('Location')
283 return response.headers.get('Location')
280
284
281 @line_magic
285 @line_magic
282 def loadpy(self, arg_s):
286 def loadpy(self, arg_s):
283 """Alias of `%load`
287 """Alias of `%load`
284
288
285 `%loadpy` has gained some flexibility and dropped the requirement of a `.py`
289 `%loadpy` has gained some flexibility and dropped the requirement of a `.py`
286 extension. So it has been renamed simply into %load. You can look at
290 extension. So it has been renamed simply into %load. You can look at
287 `%load`'s docstring for more info.
291 `%load`'s docstring for more info.
288 """
292 """
289 self.load(arg_s)
293 self.load(arg_s)
290
294
291 @line_magic
295 @line_magic
292 def load(self, arg_s):
296 def load(self, arg_s):
293 """Load code into the current frontend.
297 """Load code into the current frontend.
294
298
295 Usage:\\
299 Usage:\\
296 %load [options] source
300 %load [options] source
297
301
298 where source can be a filename, URL, input history range, macro, or
302 where source can be a filename, URL, input history range, macro, or
299 element in the user namespace
303 element in the user namespace
300
304
301 Options:
305 Options:
302
306
303 -r <lines>: Specify lines or ranges of lines to load from the source.
307 -r <lines>: Specify lines or ranges of lines to load from the source.
304 Ranges could be specified as x-y (x..y) or in python-style x:y
308 Ranges could be specified as x-y (x..y) or in python-style x:y
305 (x..(y-1)). Both limits x and y can be left blank (meaning the
309 (x..(y-1)). Both limits x and y can be left blank (meaning the
306 beginning and end of the file, respectively).
310 beginning and end of the file, respectively).
307
311
308 -s <symbols>: Specify function or classes to load from python source.
312 -s <symbols>: Specify function or classes to load from python source.
309
313
310 -y : Don't ask confirmation for loading source above 200 000 characters.
314 -y : Don't ask confirmation for loading source above 200 000 characters.
311
315
312 -n : Include the user's namespace when searching for source code.
316 -n : Include the user's namespace when searching for source code.
313
317
314 This magic command can either take a local filename, a URL, an history
318 This magic command can either take a local filename, a URL, an history
315 range (see %history) or a macro as argument, it will prompt for
319 range (see %history) or a macro as argument, it will prompt for
316 confirmation before loading source with more than 200 000 characters, unless
320 confirmation before loading source with more than 200 000 characters, unless
317 -y flag is passed or if the frontend does not support raw_input::
321 -y flag is passed or if the frontend does not support raw_input::
318
322
319 %load myscript.py
323 %load myscript.py
320 %load 7-27
324 %load 7-27
321 %load myMacro
325 %load myMacro
322 %load http://www.example.com/myscript.py
326 %load http://www.example.com/myscript.py
323 %load -r 5-10 myscript.py
327 %load -r 5-10 myscript.py
324 %load -r 10-20,30,40: foo.py
328 %load -r 10-20,30,40: foo.py
325 %load -s MyClass,wonder_function myscript.py
329 %load -s MyClass,wonder_function myscript.py
326 %load -n MyClass
330 %load -n MyClass
327 %load -n my_module.wonder_function
331 %load -n my_module.wonder_function
328 """
332 """
329 opts,args = self.parse_options(arg_s,'yns:r:')
333 opts,args = self.parse_options(arg_s,'yns:r:')
330
334
331 if not args:
335 if not args:
332 raise UsageError('Missing filename, URL, input history range, '
336 raise UsageError('Missing filename, URL, input history range, '
333 'macro, or element in the user namespace.')
337 'macro, or element in the user namespace.')
334
338
335 search_ns = 'n' in opts
339 search_ns = 'n' in opts
336
340
337 contents = self.shell.find_user_code(args, search_ns=search_ns)
341 contents = self.shell.find_user_code(args, search_ns=search_ns)
338
342
339 if 's' in opts:
343 if 's' in opts:
340 try:
344 try:
341 blocks, not_found = extract_symbols(contents, opts['s'])
345 blocks, not_found = extract_symbols(contents, opts['s'])
342 except SyntaxError:
346 except SyntaxError:
343 # non python code
347 # non python code
344 error("Unable to parse the input as valid Python code")
348 error("Unable to parse the input as valid Python code")
345 return
349 return
346
350
347 if len(not_found) == 1:
351 if len(not_found) == 1:
348 warn('The symbol `%s` was not found' % not_found[0])
352 warn('The symbol `%s` was not found' % not_found[0])
349 elif len(not_found) > 1:
353 elif len(not_found) > 1:
350 warn('The symbols %s were not found' % get_text_list(not_found,
354 warn('The symbols %s were not found' % get_text_list(not_found,
351 wrap_item_with='`')
355 wrap_item_with='`')
352 )
356 )
353
357
354 contents = '\n'.join(blocks)
358 contents = '\n'.join(blocks)
355
359
356 if 'r' in opts:
360 if 'r' in opts:
357 ranges = opts['r'].replace(',', ' ')
361 ranges = opts['r'].replace(',', ' ')
358 lines = contents.split('\n')
362 lines = contents.split('\n')
359 slices = extract_code_ranges(ranges)
363 slices = extract_code_ranges(ranges)
360 contents = [lines[slice(*slc)] for slc in slices]
364 contents = [lines[slice(*slc)] for slc in slices]
361 contents = '\n'.join(strip_initial_indent(chain.from_iterable(contents)))
365 contents = '\n'.join(strip_initial_indent(chain.from_iterable(contents)))
362
366
363 l = len(contents)
367 l = len(contents)
364
368
365 # 200 000 is ~ 2500 full 80 character lines
369 # 200 000 is ~ 2500 full 80 character lines
366 # so in average, more than 5000 lines
370 # so in average, more than 5000 lines
367 if l > 200000 and 'y' not in opts:
371 if l > 200000 and 'y' not in opts:
368 try:
372 try:
369 ans = self.shell.ask_yes_no(("The text you're trying to load seems pretty big"\
373 ans = self.shell.ask_yes_no(("The text you're trying to load seems pretty big"\
370 " (%d characters). Continue (y/[N]) ?" % l), default='n' )
374 " (%d characters). Continue (y/[N]) ?" % l), default='n' )
371 except StdinNotImplementedError:
375 except StdinNotImplementedError:
372 #assume yes if raw input not implemented
376 #assume yes if raw input not implemented
373 ans = True
377 ans = True
374
378
375 if ans is False :
379 if ans is False :
376 print('Operation cancelled.')
380 print('Operation cancelled.')
377 return
381 return
378
382
379 contents = "# %load {}\n".format(arg_s) + contents
383 contents = "# %load {}\n".format(arg_s) + contents
380
384
381 self.shell.set_next_input(contents, replace=True)
385 self.shell.set_next_input(contents, replace=True)
382
386
383 @staticmethod
387 @staticmethod
384 def _find_edit_target(shell, args, opts, last_call):
388 def _find_edit_target(shell, args, opts, last_call):
385 """Utility method used by magic_edit to find what to edit."""
389 """Utility method used by magic_edit to find what to edit."""
386
390
387 def make_filename(arg):
391 def make_filename(arg):
388 "Make a filename from the given args"
392 "Make a filename from the given args"
389 try:
393 try:
390 filename = get_py_filename(arg)
394 filename = get_py_filename(arg)
391 except IOError:
395 except IOError:
392 # If it ends with .py but doesn't already exist, assume we want
396 # If it ends with .py but doesn't already exist, assume we want
393 # a new file.
397 # a new file.
394 if arg.endswith('.py'):
398 if arg.endswith('.py'):
395 filename = arg
399 filename = arg
396 else:
400 else:
397 filename = None
401 filename = None
398 return filename
402 return filename
399
403
400 # Set a few locals from the options for convenience:
404 # Set a few locals from the options for convenience:
401 opts_prev = 'p' in opts
405 opts_prev = 'p' in opts
402 opts_raw = 'r' in opts
406 opts_raw = 'r' in opts
403
407
404 # custom exceptions
408 # custom exceptions
405 class DataIsObject(Exception): pass
409 class DataIsObject(Exception): pass
406
410
407 # Default line number value
411 # Default line number value
408 lineno = opts.get('n',None)
412 lineno = opts.get('n',None)
409
413
410 if opts_prev:
414 if opts_prev:
411 args = '_%s' % last_call[0]
415 args = '_%s' % last_call[0]
412 if args not in shell.user_ns:
416 if args not in shell.user_ns:
413 args = last_call[1]
417 args = last_call[1]
414
418
415 # by default this is done with temp files, except when the given
419 # by default this is done with temp files, except when the given
416 # arg is a filename
420 # arg is a filename
417 use_temp = True
421 use_temp = True
418
422
419 data = ''
423 data = ''
420
424
421 # First, see if the arguments should be a filename.
425 # First, see if the arguments should be a filename.
422 filename = make_filename(args)
426 filename = make_filename(args)
423 if filename:
427 if filename:
424 use_temp = False
428 use_temp = False
425 elif args:
429 elif args:
426 # Mode where user specifies ranges of lines, like in %macro.
430 # Mode where user specifies ranges of lines, like in %macro.
427 data = shell.extract_input_lines(args, opts_raw)
431 data = shell.extract_input_lines(args, opts_raw)
428 if not data:
432 if not data:
429 try:
433 try:
430 # Load the parameter given as a variable. If not a string,
434 # Load the parameter given as a variable. If not a string,
431 # process it as an object instead (below)
435 # process it as an object instead (below)
432
436
433 #print '*** args',args,'type',type(args) # dbg
437 #print '*** args',args,'type',type(args) # dbg
434 data = eval(args, shell.user_ns)
438 data = eval(args, shell.user_ns)
435 if not isinstance(data, str):
439 if not isinstance(data, str):
436 raise DataIsObject
440 raise DataIsObject
437
441
438 except (NameError,SyntaxError):
442 except (NameError,SyntaxError):
439 # given argument is not a variable, try as a filename
443 # given argument is not a variable, try as a filename
440 filename = make_filename(args)
444 filename = make_filename(args)
441 if filename is None:
445 if filename is None:
442 warn("Argument given (%s) can't be found as a variable "
446 warn("Argument given (%s) can't be found as a variable "
443 "or as a filename." % args)
447 "or as a filename." % args)
444 return (None, None, None)
448 return (None, None, None)
445 use_temp = False
449 use_temp = False
446
450
447 except DataIsObject as e:
451 except DataIsObject as e:
448 # macros have a special edit function
452 # macros have a special edit function
449 if isinstance(data, Macro):
453 if isinstance(data, Macro):
450 raise MacroToEdit(data) from e
454 raise MacroToEdit(data) from e
451
455
452 # For objects, try to edit the file where they are defined
456 # For objects, try to edit the file where they are defined
453 filename = find_file(data)
457 filename = find_file(data)
454 if filename:
458 if filename:
455 if 'fakemodule' in filename.lower() and \
459 if 'fakemodule' in filename.lower() and \
456 inspect.isclass(data):
460 inspect.isclass(data):
457 # class created by %edit? Try to find source
461 # class created by %edit? Try to find source
458 # by looking for method definitions instead, the
462 # by looking for method definitions instead, the
459 # __module__ in those classes is FakeModule.
463 # __module__ in those classes is FakeModule.
460 attrs = [getattr(data, aname) for aname in dir(data)]
464 attrs = [getattr(data, aname) for aname in dir(data)]
461 for attr in attrs:
465 for attr in attrs:
462 if not inspect.ismethod(attr):
466 if not inspect.ismethod(attr):
463 continue
467 continue
464 filename = find_file(attr)
468 filename = find_file(attr)
465 if filename and \
469 if filename and \
466 'fakemodule' not in filename.lower():
470 'fakemodule' not in filename.lower():
467 # change the attribute to be the edit
471 # change the attribute to be the edit
468 # target instead
472 # target instead
469 data = attr
473 data = attr
470 break
474 break
471
475
472 m = ipython_input_pat.match(os.path.basename(filename))
476 m = ipython_input_pat.match(os.path.basename(filename))
473 if m:
477 if m:
474 raise InteractivelyDefined(int(m.groups()[0])) from e
478 raise InteractivelyDefined(int(m.groups()[0])) from e
475
479
476 datafile = 1
480 datafile = 1
477 if filename is None:
481 if filename is None:
478 filename = make_filename(args)
482 filename = make_filename(args)
479 datafile = 1
483 datafile = 1
480 if filename is not None:
484 if filename is not None:
481 # only warn about this if we get a real name
485 # only warn about this if we get a real name
482 warn('Could not find file where `%s` is defined.\n'
486 warn('Could not find file where `%s` is defined.\n'
483 'Opening a file named `%s`' % (args, filename))
487 'Opening a file named `%s`' % (args, filename))
484 # Now, make sure we can actually read the source (if it was
488 # Now, make sure we can actually read the source (if it was
485 # in a temp file it's gone by now).
489 # in a temp file it's gone by now).
486 if datafile:
490 if datafile:
487 if lineno is None:
491 if lineno is None:
488 lineno = find_source_lines(data)
492 lineno = find_source_lines(data)
489 if lineno is None:
493 if lineno is None:
490 filename = make_filename(args)
494 filename = make_filename(args)
491 if filename is None:
495 if filename is None:
492 warn('The file where `%s` was defined '
496 warn('The file where `%s` was defined '
493 'cannot be read or found.' % data)
497 'cannot be read or found.' % data)
494 return (None, None, None)
498 return (None, None, None)
495 use_temp = False
499 use_temp = False
496
500
497 if use_temp:
501 if use_temp:
498 filename = shell.mktempfile(data)
502 filename = shell.mktempfile(data)
499 print('IPython will make a temporary file named:',filename)
503 print('IPython will make a temporary file named:',filename)
500
504
501 # use last_call to remember the state of the previous call, but don't
505 # use last_call to remember the state of the previous call, but don't
502 # let it be clobbered by successive '-p' calls.
506 # let it be clobbered by successive '-p' calls.
503 try:
507 try:
504 last_call[0] = shell.displayhook.prompt_count
508 last_call[0] = shell.displayhook.prompt_count
505 if not opts_prev:
509 if not opts_prev:
506 last_call[1] = args
510 last_call[1] = args
507 except:
511 except:
508 pass
512 pass
509
513
510
514
511 return filename, lineno, use_temp
515 return filename, lineno, use_temp
512
516
513 def _edit_macro(self,mname,macro):
517 def _edit_macro(self,mname,macro):
514 """open an editor with the macro data in a file"""
518 """open an editor with the macro data in a file"""
515 filename = self.shell.mktempfile(macro.value)
519 filename = self.shell.mktempfile(macro.value)
516 self.shell.hooks.editor(filename)
520 self.shell.hooks.editor(filename)
517
521
518 # and make a new macro object, to replace the old one
522 # and make a new macro object, to replace the old one
519 mvalue = Path(filename).read_text()
523 mvalue = Path(filename).read_text()
520 self.shell.user_ns[mname] = Macro(mvalue)
524 self.shell.user_ns[mname] = Macro(mvalue)
521
525
522 @skip_doctest
526 @skip_doctest
523 @line_magic
527 @line_magic
524 def edit(self, parameter_s='',last_call=['','']):
528 def edit(self, parameter_s='',last_call=['','']):
525 """Bring up an editor and execute the resulting code.
529 """Bring up an editor and execute the resulting code.
526
530
527 Usage:
531 Usage:
528 %edit [options] [args]
532 %edit [options] [args]
529
533
530 %edit runs IPython's editor hook. The default version of this hook is
534 %edit runs IPython's editor hook. The default version of this hook is
531 set to call the editor specified by your $EDITOR environment variable.
535 set to call the editor specified by your $EDITOR environment variable.
532 If this isn't found, it will default to vi under Linux/Unix and to
536 If this isn't found, it will default to vi under Linux/Unix and to
533 notepad under Windows. See the end of this docstring for how to change
537 notepad under Windows. See the end of this docstring for how to change
534 the editor hook.
538 the editor hook.
535
539
536 You can also set the value of this editor via the
540 You can also set the value of this editor via the
537 ``TerminalInteractiveShell.editor`` option in your configuration file.
541 ``TerminalInteractiveShell.editor`` option in your configuration file.
538 This is useful if you wish to use a different editor from your typical
542 This is useful if you wish to use a different editor from your typical
539 default with IPython (and for Windows users who typically don't set
543 default with IPython (and for Windows users who typically don't set
540 environment variables).
544 environment variables).
541
545
542 This command allows you to conveniently edit multi-line code right in
546 This command allows you to conveniently edit multi-line code right in
543 your IPython session.
547 your IPython session.
544
548
545 If called without arguments, %edit opens up an empty editor with a
549 If called without arguments, %edit opens up an empty editor with a
546 temporary file and will execute the contents of this file when you
550 temporary file and will execute the contents of this file when you
547 close it (don't forget to save it!).
551 close it (don't forget to save it!).
548
552
549
553
550 Options:
554 Options:
551
555
552 -n <number>: open the editor at a specified line number. By default,
556 -n <number>: open the editor at a specified line number. By default,
553 the IPython editor hook uses the unix syntax 'editor +N filename', but
557 the IPython editor hook uses the unix syntax 'editor +N filename', but
554 you can configure this by providing your own modified hook if your
558 you can configure this by providing your own modified hook if your
555 favorite editor supports line-number specifications with a different
559 favorite editor supports line-number specifications with a different
556 syntax.
560 syntax.
557
561
558 -p: this will call the editor with the same data as the previous time
562 -p: this will call the editor with the same data as the previous time
559 it was used, regardless of how long ago (in your current session) it
563 it was used, regardless of how long ago (in your current session) it
560 was.
564 was.
561
565
562 -r: use 'raw' input. This option only applies to input taken from the
566 -r: use 'raw' input. This option only applies to input taken from the
563 user's history. By default, the 'processed' history is used, so that
567 user's history. By default, the 'processed' history is used, so that
564 magics are loaded in their transformed version to valid Python. If
568 magics are loaded in their transformed version to valid Python. If
565 this option is given, the raw input as typed as the command line is
569 this option is given, the raw input as typed as the command line is
566 used instead. When you exit the editor, it will be executed by
570 used instead. When you exit the editor, it will be executed by
567 IPython's own processor.
571 IPython's own processor.
568
572
569 -x: do not execute the edited code immediately upon exit. This is
573 -x: do not execute the edited code immediately upon exit. This is
570 mainly useful if you are editing programs which need to be called with
574 mainly useful if you are editing programs which need to be called with
571 command line arguments, which you can then do using %run.
575 command line arguments, which you can then do using %run.
572
576
573
577
574 Arguments:
578 Arguments:
575
579
576 If arguments are given, the following possibilities exist:
580 If arguments are given, the following possibilities exist:
577
581
578 - If the argument is a filename, IPython will load that into the
582 - If the argument is a filename, IPython will load that into the
579 editor. It will execute its contents with execfile() when you exit,
583 editor. It will execute its contents with execfile() when you exit,
580 loading any code in the file into your interactive namespace.
584 loading any code in the file into your interactive namespace.
581
585
582 - The arguments are ranges of input history, e.g. "7 ~1/4-6".
586 - The arguments are ranges of input history, e.g. "7 ~1/4-6".
583 The syntax is the same as in the %history magic.
587 The syntax is the same as in the %history magic.
584
588
585 - If the argument is a string variable, its contents are loaded
589 - If the argument is a string variable, its contents are loaded
586 into the editor. You can thus edit any string which contains
590 into the editor. You can thus edit any string which contains
587 python code (including the result of previous edits).
591 python code (including the result of previous edits).
588
592
589 - If the argument is the name of an object (other than a string),
593 - If the argument is the name of an object (other than a string),
590 IPython will try to locate the file where it was defined and open the
594 IPython will try to locate the file where it was defined and open the
591 editor at the point where it is defined. You can use `%edit function`
595 editor at the point where it is defined. You can use `%edit function`
592 to load an editor exactly at the point where 'function' is defined,
596 to load an editor exactly at the point where 'function' is defined,
593 edit it and have the file be executed automatically.
597 edit it and have the file be executed automatically.
594
598
595 - If the object is a macro (see %macro for details), this opens up your
599 - If the object is a macro (see %macro for details), this opens up your
596 specified editor with a temporary file containing the macro's data.
600 specified editor with a temporary file containing the macro's data.
597 Upon exit, the macro is reloaded with the contents of the file.
601 Upon exit, the macro is reloaded with the contents of the file.
598
602
599 Note: opening at an exact line is only supported under Unix, and some
603 Note: opening at an exact line is only supported under Unix, and some
600 editors (like kedit and gedit up to Gnome 2.8) do not understand the
604 editors (like kedit and gedit up to Gnome 2.8) do not understand the
601 '+NUMBER' parameter necessary for this feature. Good editors like
605 '+NUMBER' parameter necessary for this feature. Good editors like
602 (X)Emacs, vi, jed, pico and joe all do.
606 (X)Emacs, vi, jed, pico and joe all do.
603
607
604 After executing your code, %edit will return as output the code you
608 After executing your code, %edit will return as output the code you
605 typed in the editor (except when it was an existing file). This way
609 typed in the editor (except when it was an existing file). This way
606 you can reload the code in further invocations of %edit as a variable,
610 you can reload the code in further invocations of %edit as a variable,
607 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
611 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
608 the output.
612 the output.
609
613
610 Note that %edit is also available through the alias %ed.
614 Note that %edit is also available through the alias %ed.
611
615
612 This is an example of creating a simple function inside the editor and
616 This is an example of creating a simple function inside the editor and
613 then modifying it. First, start up the editor::
617 then modifying it. First, start up the editor::
614
618
615 In [1]: edit
619 In [1]: edit
616 Editing... done. Executing edited code...
620 Editing... done. Executing edited code...
617 Out[1]: 'def foo():\\n print "foo() was defined in an editing
621 Out[1]: 'def foo():\\n print "foo() was defined in an editing
618 session"\\n'
622 session"\\n'
619
623
620 We can then call the function foo()::
624 We can then call the function foo()::
621
625
622 In [2]: foo()
626 In [2]: foo()
623 foo() was defined in an editing session
627 foo() was defined in an editing session
624
628
625 Now we edit foo. IPython automatically loads the editor with the
629 Now we edit foo. IPython automatically loads the editor with the
626 (temporary) file where foo() was previously defined::
630 (temporary) file where foo() was previously defined::
627
631
628 In [3]: edit foo
632 In [3]: edit foo
629 Editing... done. Executing edited code...
633 Editing... done. Executing edited code...
630
634
631 And if we call foo() again we get the modified version::
635 And if we call foo() again we get the modified version::
632
636
633 In [4]: foo()
637 In [4]: foo()
634 foo() has now been changed!
638 foo() has now been changed!
635
639
636 Here is an example of how to edit a code snippet successive
640 Here is an example of how to edit a code snippet successive
637 times. First we call the editor::
641 times. First we call the editor::
638
642
639 In [5]: edit
643 In [5]: edit
640 Editing... done. Executing edited code...
644 Editing... done. Executing edited code...
641 hello
645 hello
642 Out[5]: "print 'hello'\\n"
646 Out[5]: "print 'hello'\\n"
643
647
644 Now we call it again with the previous output (stored in _)::
648 Now we call it again with the previous output (stored in _)::
645
649
646 In [6]: edit _
650 In [6]: edit _
647 Editing... done. Executing edited code...
651 Editing... done. Executing edited code...
648 hello world
652 hello world
649 Out[6]: "print 'hello world'\\n"
653 Out[6]: "print 'hello world'\\n"
650
654
651 Now we call it with the output #8 (stored in _8, also as Out[8])::
655 Now we call it with the output #8 (stored in _8, also as Out[8])::
652
656
653 In [7]: edit _8
657 In [7]: edit _8
654 Editing... done. Executing edited code...
658 Editing... done. Executing edited code...
655 hello again
659 hello again
656 Out[7]: "print 'hello again'\\n"
660 Out[7]: "print 'hello again'\\n"
657
661
658
662
659 Changing the default editor hook:
663 Changing the default editor hook:
660
664
661 If you wish to write your own editor hook, you can put it in a
665 If you wish to write your own editor hook, you can put it in a
662 configuration file which you load at startup time. The default hook
666 configuration file which you load at startup time. The default hook
663 is defined in the IPython.core.hooks module, and you can use that as a
667 is defined in the IPython.core.hooks module, and you can use that as a
664 starting example for further modifications. That file also has
668 starting example for further modifications. That file also has
665 general instructions on how to set a new hook for use once you've
669 general instructions on how to set a new hook for use once you've
666 defined it."""
670 defined it."""
667 opts,args = self.parse_options(parameter_s,'prxn:')
671 opts,args = self.parse_options(parameter_s,'prxn:')
668
672
669 try:
673 try:
670 filename, lineno, is_temp = self._find_edit_target(self.shell,
674 filename, lineno, is_temp = self._find_edit_target(self.shell,
671 args, opts, last_call)
675 args, opts, last_call)
672 except MacroToEdit as e:
676 except MacroToEdit as e:
673 self._edit_macro(args, e.args[0])
677 self._edit_macro(args, e.args[0])
674 return
678 return
675 except InteractivelyDefined as e:
679 except InteractivelyDefined as e:
676 print("Editing In[%i]" % e.index)
680 print("Editing In[%i]" % e.index)
677 args = str(e.index)
681 args = str(e.index)
678 filename, lineno, is_temp = self._find_edit_target(self.shell,
682 filename, lineno, is_temp = self._find_edit_target(self.shell,
679 args, opts, last_call)
683 args, opts, last_call)
680 if filename is None:
684 if filename is None:
681 # nothing was found, warnings have already been issued,
685 # nothing was found, warnings have already been issued,
682 # just give up.
686 # just give up.
683 return
687 return
684
688
685 if is_temp:
689 if is_temp:
686 self._knowntemps.add(filename)
690 self._knowntemps.add(filename)
687 elif (filename in self._knowntemps):
691 elif (filename in self._knowntemps):
688 is_temp = True
692 is_temp = True
689
693
690
694
691 # do actual editing here
695 # do actual editing here
692 print('Editing...', end=' ')
696 print('Editing...', end=' ')
693 sys.stdout.flush()
697 sys.stdout.flush()
694 filepath = Path(filename)
698 filepath = Path(filename)
695 try:
699 try:
696 # Quote filenames that may have spaces in them when opening
700 # Quote filenames that may have spaces in them when opening
697 # the editor
701 # the editor
698 quoted = filename = str(filepath.absolute())
702 quoted = filename = str(filepath.absolute())
699 if " " in quoted:
703 if " " in quoted:
700 quoted = "'%s'" % quoted
704 quoted = "'%s'" % quoted
701 self.shell.hooks.editor(quoted, lineno)
705 self.shell.hooks.editor(quoted, lineno)
702 except TryNext:
706 except TryNext:
703 warn('Could not open editor')
707 warn('Could not open editor')
704 return
708 return
705
709
706 # XXX TODO: should this be generalized for all string vars?
710 # XXX TODO: should this be generalized for all string vars?
707 # For now, this is special-cased to blocks created by cpaste
711 # For now, this is special-cased to blocks created by cpaste
708 if args.strip() == "pasted_block":
712 if args.strip() == "pasted_block":
709 self.shell.user_ns["pasted_block"] = filepath.read_text()
713 self.shell.user_ns["pasted_block"] = filepath.read_text()
710
714
711 if 'x' in opts: # -x prevents actual execution
715 if 'x' in opts: # -x prevents actual execution
712 print()
716 print()
713 else:
717 else:
714 print('done. Executing edited code...')
718 print('done. Executing edited code...')
715 with preserve_keys(self.shell.user_ns, '__file__'):
719 with preserve_keys(self.shell.user_ns, '__file__'):
716 if not is_temp:
720 if not is_temp:
717 self.shell.user_ns['__file__'] = filename
721 self.shell.user_ns['__file__'] = filename
718 if 'r' in opts: # Untranslated IPython code
722 if 'r' in opts: # Untranslated IPython code
719 source = filepath.read_text()
723 source = filepath.read_text()
720 self.shell.run_cell(source, store_history=False)
724 self.shell.run_cell(source, store_history=False)
721 else:
725 else:
722 self.shell.safe_execfile(filename, self.shell.user_ns,
726 self.shell.safe_execfile(filename, self.shell.user_ns,
723 self.shell.user_ns)
727 self.shell.user_ns)
724
728
725 if is_temp:
729 if is_temp:
726 try:
730 try:
727 return filepath.read_text()
731 return filepath.read_text()
728 except IOError as msg:
732 except IOError as msg:
729 if Path(msg.filename) == filepath:
733 if Path(msg.filename) == filepath:
730 warn('File not found. Did you forget to save?')
734 warn('File not found. Did you forget to save?')
731 return
735 return
732 else:
736 else:
733 self.shell.showtraceback()
737 self.shell.showtraceback()
General Comments 0
You need to be logged in to leave comments. Login now