##// END OF EJS Templates
Disambiguated docstring, since there have been other dpastes
Paul Bissex -
Show More
@@ -1,733 +1,733 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's paste bin, 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 "title": opts.get('d', "Pasted from IPython"),
272 "syntax": "python",
272 "syntax": "python",
273 "content": code
273 "content": code
274 }).encode('utf-8')
274 }).encode('utf-8')
275
275
276 request = Request("http://dpaste.com/api/v2/",
276 request = Request("http://dpaste.com/api/v2/",
277 headers={"User-Agent": "IPython v{}".format(version)})
277 headers={"User-Agent": "IPython v{}".format(version)})
278 response = urlopen(request, post_data)
278 response = urlopen(request, post_data)
279 return response.headers.get('Location')
279 return response.headers.get('Location')
280
280
281 @line_magic
281 @line_magic
282 def loadpy(self, arg_s):
282 def loadpy(self, arg_s):
283 """Alias of `%load`
283 """Alias of `%load`
284
284
285 `%loadpy` has gained some flexibility and dropped the requirement of a `.py`
285 `%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
286 extension. So it has been renamed simply into %load. You can look at
287 `%load`'s docstring for more info.
287 `%load`'s docstring for more info.
288 """
288 """
289 self.load(arg_s)
289 self.load(arg_s)
290
290
291 @line_magic
291 @line_magic
292 def load(self, arg_s):
292 def load(self, arg_s):
293 """Load code into the current frontend.
293 """Load code into the current frontend.
294
294
295 Usage:\\
295 Usage:\\
296 %load [options] source
296 %load [options] source
297
297
298 where source can be a filename, URL, input history range, macro, or
298 where source can be a filename, URL, input history range, macro, or
299 element in the user namespace
299 element in the user namespace
300
300
301 Options:
301 Options:
302
302
303 -r <lines>: Specify lines or ranges of lines to load from the source.
303 -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
304 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
305 (x..(y-1)). Both limits x and y can be left blank (meaning the
306 beginning and end of the file, respectively).
306 beginning and end of the file, respectively).
307
307
308 -s <symbols>: Specify function or classes to load from python source.
308 -s <symbols>: Specify function or classes to load from python source.
309
309
310 -y : Don't ask confirmation for loading source above 200 000 characters.
310 -y : Don't ask confirmation for loading source above 200 000 characters.
311
311
312 -n : Include the user's namespace when searching for source code.
312 -n : Include the user's namespace when searching for source code.
313
313
314 This magic command can either take a local filename, a URL, an history
314 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
315 range (see %history) or a macro as argument, it will prompt for
316 confirmation before loading source with more than 200 000 characters, unless
316 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::
317 -y flag is passed or if the frontend does not support raw_input::
318
318
319 %load myscript.py
319 %load myscript.py
320 %load 7-27
320 %load 7-27
321 %load myMacro
321 %load myMacro
322 %load http://www.example.com/myscript.py
322 %load http://www.example.com/myscript.py
323 %load -r 5-10 myscript.py
323 %load -r 5-10 myscript.py
324 %load -r 10-20,30,40: foo.py
324 %load -r 10-20,30,40: foo.py
325 %load -s MyClass,wonder_function myscript.py
325 %load -s MyClass,wonder_function myscript.py
326 %load -n MyClass
326 %load -n MyClass
327 %load -n my_module.wonder_function
327 %load -n my_module.wonder_function
328 """
328 """
329 opts,args = self.parse_options(arg_s,'yns:r:')
329 opts,args = self.parse_options(arg_s,'yns:r:')
330
330
331 if not args:
331 if not args:
332 raise UsageError('Missing filename, URL, input history range, '
332 raise UsageError('Missing filename, URL, input history range, '
333 'macro, or element in the user namespace.')
333 'macro, or element in the user namespace.')
334
334
335 search_ns = 'n' in opts
335 search_ns = 'n' in opts
336
336
337 contents = self.shell.find_user_code(args, search_ns=search_ns)
337 contents = self.shell.find_user_code(args, search_ns=search_ns)
338
338
339 if 's' in opts:
339 if 's' in opts:
340 try:
340 try:
341 blocks, not_found = extract_symbols(contents, opts['s'])
341 blocks, not_found = extract_symbols(contents, opts['s'])
342 except SyntaxError:
342 except SyntaxError:
343 # non python code
343 # non python code
344 error("Unable to parse the input as valid Python code")
344 error("Unable to parse the input as valid Python code")
345 return
345 return
346
346
347 if len(not_found) == 1:
347 if len(not_found) == 1:
348 warn('The symbol `%s` was not found' % not_found[0])
348 warn('The symbol `%s` was not found' % not_found[0])
349 elif len(not_found) > 1:
349 elif len(not_found) > 1:
350 warn('The symbols %s were not found' % get_text_list(not_found,
350 warn('The symbols %s were not found' % get_text_list(not_found,
351 wrap_item_with='`')
351 wrap_item_with='`')
352 )
352 )
353
353
354 contents = '\n'.join(blocks)
354 contents = '\n'.join(blocks)
355
355
356 if 'r' in opts:
356 if 'r' in opts:
357 ranges = opts['r'].replace(',', ' ')
357 ranges = opts['r'].replace(',', ' ')
358 lines = contents.split('\n')
358 lines = contents.split('\n')
359 slices = extract_code_ranges(ranges)
359 slices = extract_code_ranges(ranges)
360 contents = [lines[slice(*slc)] for slc in slices]
360 contents = [lines[slice(*slc)] for slc in slices]
361 contents = '\n'.join(strip_initial_indent(chain.from_iterable(contents)))
361 contents = '\n'.join(strip_initial_indent(chain.from_iterable(contents)))
362
362
363 l = len(contents)
363 l = len(contents)
364
364
365 # 200 000 is ~ 2500 full 80 character lines
365 # 200 000 is ~ 2500 full 80 character lines
366 # so in average, more than 5000 lines
366 # so in average, more than 5000 lines
367 if l > 200000 and 'y' not in opts:
367 if l > 200000 and 'y' not in opts:
368 try:
368 try:
369 ans = self.shell.ask_yes_no(("The text you're trying to load seems pretty big"\
369 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' )
370 " (%d characters). Continue (y/[N]) ?" % l), default='n' )
371 except StdinNotImplementedError:
371 except StdinNotImplementedError:
372 #assume yes if raw input not implemented
372 #assume yes if raw input not implemented
373 ans = True
373 ans = True
374
374
375 if ans is False :
375 if ans is False :
376 print('Operation cancelled.')
376 print('Operation cancelled.')
377 return
377 return
378
378
379 contents = "# %load {}\n".format(arg_s) + contents
379 contents = "# %load {}\n".format(arg_s) + contents
380
380
381 self.shell.set_next_input(contents, replace=True)
381 self.shell.set_next_input(contents, replace=True)
382
382
383 @staticmethod
383 @staticmethod
384 def _find_edit_target(shell, args, opts, last_call):
384 def _find_edit_target(shell, args, opts, last_call):
385 """Utility method used by magic_edit to find what to edit."""
385 """Utility method used by magic_edit to find what to edit."""
386
386
387 def make_filename(arg):
387 def make_filename(arg):
388 "Make a filename from the given args"
388 "Make a filename from the given args"
389 try:
389 try:
390 filename = get_py_filename(arg)
390 filename = get_py_filename(arg)
391 except IOError:
391 except IOError:
392 # If it ends with .py but doesn't already exist, assume we want
392 # If it ends with .py but doesn't already exist, assume we want
393 # a new file.
393 # a new file.
394 if arg.endswith('.py'):
394 if arg.endswith('.py'):
395 filename = arg
395 filename = arg
396 else:
396 else:
397 filename = None
397 filename = None
398 return filename
398 return filename
399
399
400 # Set a few locals from the options for convenience:
400 # Set a few locals from the options for convenience:
401 opts_prev = 'p' in opts
401 opts_prev = 'p' in opts
402 opts_raw = 'r' in opts
402 opts_raw = 'r' in opts
403
403
404 # custom exceptions
404 # custom exceptions
405 class DataIsObject(Exception): pass
405 class DataIsObject(Exception): pass
406
406
407 # Default line number value
407 # Default line number value
408 lineno = opts.get('n',None)
408 lineno = opts.get('n',None)
409
409
410 if opts_prev:
410 if opts_prev:
411 args = '_%s' % last_call[0]
411 args = '_%s' % last_call[0]
412 if args not in shell.user_ns:
412 if args not in shell.user_ns:
413 args = last_call[1]
413 args = last_call[1]
414
414
415 # by default this is done with temp files, except when the given
415 # by default this is done with temp files, except when the given
416 # arg is a filename
416 # arg is a filename
417 use_temp = True
417 use_temp = True
418
418
419 data = ''
419 data = ''
420
420
421 # First, see if the arguments should be a filename.
421 # First, see if the arguments should be a filename.
422 filename = make_filename(args)
422 filename = make_filename(args)
423 if filename:
423 if filename:
424 use_temp = False
424 use_temp = False
425 elif args:
425 elif args:
426 # Mode where user specifies ranges of lines, like in %macro.
426 # Mode where user specifies ranges of lines, like in %macro.
427 data = shell.extract_input_lines(args, opts_raw)
427 data = shell.extract_input_lines(args, opts_raw)
428 if not data:
428 if not data:
429 try:
429 try:
430 # Load the parameter given as a variable. If not a string,
430 # Load the parameter given as a variable. If not a string,
431 # process it as an object instead (below)
431 # process it as an object instead (below)
432
432
433 #print '*** args',args,'type',type(args) # dbg
433 #print '*** args',args,'type',type(args) # dbg
434 data = eval(args, shell.user_ns)
434 data = eval(args, shell.user_ns)
435 if not isinstance(data, str):
435 if not isinstance(data, str):
436 raise DataIsObject
436 raise DataIsObject
437
437
438 except (NameError,SyntaxError):
438 except (NameError,SyntaxError):
439 # given argument is not a variable, try as a filename
439 # given argument is not a variable, try as a filename
440 filename = make_filename(args)
440 filename = make_filename(args)
441 if filename is None:
441 if filename is None:
442 warn("Argument given (%s) can't be found as a variable "
442 warn("Argument given (%s) can't be found as a variable "
443 "or as a filename." % args)
443 "or as a filename." % args)
444 return (None, None, None)
444 return (None, None, None)
445 use_temp = False
445 use_temp = False
446
446
447 except DataIsObject as e:
447 except DataIsObject as e:
448 # macros have a special edit function
448 # macros have a special edit function
449 if isinstance(data, Macro):
449 if isinstance(data, Macro):
450 raise MacroToEdit(data) from e
450 raise MacroToEdit(data) from e
451
451
452 # For objects, try to edit the file where they are defined
452 # For objects, try to edit the file where they are defined
453 filename = find_file(data)
453 filename = find_file(data)
454 if filename:
454 if filename:
455 if 'fakemodule' in filename.lower() and \
455 if 'fakemodule' in filename.lower() and \
456 inspect.isclass(data):
456 inspect.isclass(data):
457 # class created by %edit? Try to find source
457 # class created by %edit? Try to find source
458 # by looking for method definitions instead, the
458 # by looking for method definitions instead, the
459 # __module__ in those classes is FakeModule.
459 # __module__ in those classes is FakeModule.
460 attrs = [getattr(data, aname) for aname in dir(data)]
460 attrs = [getattr(data, aname) for aname in dir(data)]
461 for attr in attrs:
461 for attr in attrs:
462 if not inspect.ismethod(attr):
462 if not inspect.ismethod(attr):
463 continue
463 continue
464 filename = find_file(attr)
464 filename = find_file(attr)
465 if filename and \
465 if filename and \
466 'fakemodule' not in filename.lower():
466 'fakemodule' not in filename.lower():
467 # change the attribute to be the edit
467 # change the attribute to be the edit
468 # target instead
468 # target instead
469 data = attr
469 data = attr
470 break
470 break
471
471
472 m = ipython_input_pat.match(os.path.basename(filename))
472 m = ipython_input_pat.match(os.path.basename(filename))
473 if m:
473 if m:
474 raise InteractivelyDefined(int(m.groups()[0])) from e
474 raise InteractivelyDefined(int(m.groups()[0])) from e
475
475
476 datafile = 1
476 datafile = 1
477 if filename is None:
477 if filename is None:
478 filename = make_filename(args)
478 filename = make_filename(args)
479 datafile = 1
479 datafile = 1
480 if filename is not None:
480 if filename is not None:
481 # only warn about this if we get a real name
481 # only warn about this if we get a real name
482 warn('Could not find file where `%s` is defined.\n'
482 warn('Could not find file where `%s` is defined.\n'
483 'Opening a file named `%s`' % (args, filename))
483 'Opening a file named `%s`' % (args, filename))
484 # Now, make sure we can actually read the source (if it was
484 # Now, make sure we can actually read the source (if it was
485 # in a temp file it's gone by now).
485 # in a temp file it's gone by now).
486 if datafile:
486 if datafile:
487 if lineno is None:
487 if lineno is None:
488 lineno = find_source_lines(data)
488 lineno = find_source_lines(data)
489 if lineno is None:
489 if lineno is None:
490 filename = make_filename(args)
490 filename = make_filename(args)
491 if filename is None:
491 if filename is None:
492 warn('The file where `%s` was defined '
492 warn('The file where `%s` was defined '
493 'cannot be read or found.' % data)
493 'cannot be read or found.' % data)
494 return (None, None, None)
494 return (None, None, None)
495 use_temp = False
495 use_temp = False
496
496
497 if use_temp:
497 if use_temp:
498 filename = shell.mktempfile(data)
498 filename = shell.mktempfile(data)
499 print('IPython will make a temporary file named:',filename)
499 print('IPython will make a temporary file named:',filename)
500
500
501 # use last_call to remember the state of the previous call, but don't
501 # use last_call to remember the state of the previous call, but don't
502 # let it be clobbered by successive '-p' calls.
502 # let it be clobbered by successive '-p' calls.
503 try:
503 try:
504 last_call[0] = shell.displayhook.prompt_count
504 last_call[0] = shell.displayhook.prompt_count
505 if not opts_prev:
505 if not opts_prev:
506 last_call[1] = args
506 last_call[1] = args
507 except:
507 except:
508 pass
508 pass
509
509
510
510
511 return filename, lineno, use_temp
511 return filename, lineno, use_temp
512
512
513 def _edit_macro(self,mname,macro):
513 def _edit_macro(self,mname,macro):
514 """open an editor with the macro data in a file"""
514 """open an editor with the macro data in a file"""
515 filename = self.shell.mktempfile(macro.value)
515 filename = self.shell.mktempfile(macro.value)
516 self.shell.hooks.editor(filename)
516 self.shell.hooks.editor(filename)
517
517
518 # and make a new macro object, to replace the old one
518 # and make a new macro object, to replace the old one
519 mvalue = Path(filename).read_text()
519 mvalue = Path(filename).read_text()
520 self.shell.user_ns[mname] = Macro(mvalue)
520 self.shell.user_ns[mname] = Macro(mvalue)
521
521
522 @skip_doctest
522 @skip_doctest
523 @line_magic
523 @line_magic
524 def edit(self, parameter_s='',last_call=['','']):
524 def edit(self, parameter_s='',last_call=['','']):
525 """Bring up an editor and execute the resulting code.
525 """Bring up an editor and execute the resulting code.
526
526
527 Usage:
527 Usage:
528 %edit [options] [args]
528 %edit [options] [args]
529
529
530 %edit runs IPython's editor hook. The default version of this hook is
530 %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.
531 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
532 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
533 notepad under Windows. See the end of this docstring for how to change
534 the editor hook.
534 the editor hook.
535
535
536 You can also set the value of this editor via the
536 You can also set the value of this editor via the
537 ``TerminalInteractiveShell.editor`` option in your configuration file.
537 ``TerminalInteractiveShell.editor`` option in your configuration file.
538 This is useful if you wish to use a different editor from your typical
538 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
539 default with IPython (and for Windows users who typically don't set
540 environment variables).
540 environment variables).
541
541
542 This command allows you to conveniently edit multi-line code right in
542 This command allows you to conveniently edit multi-line code right in
543 your IPython session.
543 your IPython session.
544
544
545 If called without arguments, %edit opens up an empty editor with a
545 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
546 temporary file and will execute the contents of this file when you
547 close it (don't forget to save it!).
547 close it (don't forget to save it!).
548
548
549
549
550 Options:
550 Options:
551
551
552 -n <number>: open the editor at a specified line number. By default,
552 -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
553 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
554 you can configure this by providing your own modified hook if your
555 favorite editor supports line-number specifications with a different
555 favorite editor supports line-number specifications with a different
556 syntax.
556 syntax.
557
557
558 -p: this will call the editor with the same data as the previous time
558 -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
559 it was used, regardless of how long ago (in your current session) it
560 was.
560 was.
561
561
562 -r: use 'raw' input. This option only applies to input taken from the
562 -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
563 user's history. By default, the 'processed' history is used, so that
564 magics are loaded in their transformed version to valid Python. If
564 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
565 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
566 used instead. When you exit the editor, it will be executed by
567 IPython's own processor.
567 IPython's own processor.
568
568
569 -x: do not execute the edited code immediately upon exit. This is
569 -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
570 mainly useful if you are editing programs which need to be called with
571 command line arguments, which you can then do using %run.
571 command line arguments, which you can then do using %run.
572
572
573
573
574 Arguments:
574 Arguments:
575
575
576 If arguments are given, the following possibilities exist:
576 If arguments are given, the following possibilities exist:
577
577
578 - If the argument is a filename, IPython will load that into the
578 - If the argument is a filename, IPython will load that into the
579 editor. It will execute its contents with execfile() when you exit,
579 editor. It will execute its contents with execfile() when you exit,
580 loading any code in the file into your interactive namespace.
580 loading any code in the file into your interactive namespace.
581
581
582 - The arguments are ranges of input history, e.g. "7 ~1/4-6".
582 - The arguments are ranges of input history, e.g. "7 ~1/4-6".
583 The syntax is the same as in the %history magic.
583 The syntax is the same as in the %history magic.
584
584
585 - If the argument is a string variable, its contents are loaded
585 - If the argument is a string variable, its contents are loaded
586 into the editor. You can thus edit any string which contains
586 into the editor. You can thus edit any string which contains
587 python code (including the result of previous edits).
587 python code (including the result of previous edits).
588
588
589 - If the argument is the name of an object (other than a string),
589 - 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
590 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`
591 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,
592 to load an editor exactly at the point where 'function' is defined,
593 edit it and have the file be executed automatically.
593 edit it and have the file be executed automatically.
594
594
595 - If the object is a macro (see %macro for details), this opens up your
595 - 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.
596 specified editor with a temporary file containing the macro's data.
597 Upon exit, the macro is reloaded with the contents of the file.
597 Upon exit, the macro is reloaded with the contents of the file.
598
598
599 Note: opening at an exact line is only supported under Unix, and some
599 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
600 editors (like kedit and gedit up to Gnome 2.8) do not understand the
601 '+NUMBER' parameter necessary for this feature. Good editors like
601 '+NUMBER' parameter necessary for this feature. Good editors like
602 (X)Emacs, vi, jed, pico and joe all do.
602 (X)Emacs, vi, jed, pico and joe all do.
603
603
604 After executing your code, %edit will return as output the code you
604 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
605 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,
606 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
607 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
608 the output.
608 the output.
609
609
610 Note that %edit is also available through the alias %ed.
610 Note that %edit is also available through the alias %ed.
611
611
612 This is an example of creating a simple function inside the editor and
612 This is an example of creating a simple function inside the editor and
613 then modifying it. First, start up the editor::
613 then modifying it. First, start up the editor::
614
614
615 In [1]: edit
615 In [1]: edit
616 Editing... done. Executing edited code...
616 Editing... done. Executing edited code...
617 Out[1]: 'def foo():\\n print "foo() was defined in an editing
617 Out[1]: 'def foo():\\n print "foo() was defined in an editing
618 session"\\n'
618 session"\\n'
619
619
620 We can then call the function foo()::
620 We can then call the function foo()::
621
621
622 In [2]: foo()
622 In [2]: foo()
623 foo() was defined in an editing session
623 foo() was defined in an editing session
624
624
625 Now we edit foo. IPython automatically loads the editor with the
625 Now we edit foo. IPython automatically loads the editor with the
626 (temporary) file where foo() was previously defined::
626 (temporary) file where foo() was previously defined::
627
627
628 In [3]: edit foo
628 In [3]: edit foo
629 Editing... done. Executing edited code...
629 Editing... done. Executing edited code...
630
630
631 And if we call foo() again we get the modified version::
631 And if we call foo() again we get the modified version::
632
632
633 In [4]: foo()
633 In [4]: foo()
634 foo() has now been changed!
634 foo() has now been changed!
635
635
636 Here is an example of how to edit a code snippet successive
636 Here is an example of how to edit a code snippet successive
637 times. First we call the editor::
637 times. First we call the editor::
638
638
639 In [5]: edit
639 In [5]: edit
640 Editing... done. Executing edited code...
640 Editing... done. Executing edited code...
641 hello
641 hello
642 Out[5]: "print 'hello'\\n"
642 Out[5]: "print 'hello'\\n"
643
643
644 Now we call it again with the previous output (stored in _)::
644 Now we call it again with the previous output (stored in _)::
645
645
646 In [6]: edit _
646 In [6]: edit _
647 Editing... done. Executing edited code...
647 Editing... done. Executing edited code...
648 hello world
648 hello world
649 Out[6]: "print 'hello world'\\n"
649 Out[6]: "print 'hello world'\\n"
650
650
651 Now we call it with the output #8 (stored in _8, also as Out[8])::
651 Now we call it with the output #8 (stored in _8, also as Out[8])::
652
652
653 In [7]: edit _8
653 In [7]: edit _8
654 Editing... done. Executing edited code...
654 Editing... done. Executing edited code...
655 hello again
655 hello again
656 Out[7]: "print 'hello again'\\n"
656 Out[7]: "print 'hello again'\\n"
657
657
658
658
659 Changing the default editor hook:
659 Changing the default editor hook:
660
660
661 If you wish to write your own editor hook, you can put it in a
661 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
662 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
663 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
664 starting example for further modifications. That file also has
665 general instructions on how to set a new hook for use once you've
665 general instructions on how to set a new hook for use once you've
666 defined it."""
666 defined it."""
667 opts,args = self.parse_options(parameter_s,'prxn:')
667 opts,args = self.parse_options(parameter_s,'prxn:')
668
668
669 try:
669 try:
670 filename, lineno, is_temp = self._find_edit_target(self.shell,
670 filename, lineno, is_temp = self._find_edit_target(self.shell,
671 args, opts, last_call)
671 args, opts, last_call)
672 except MacroToEdit as e:
672 except MacroToEdit as e:
673 self._edit_macro(args, e.args[0])
673 self._edit_macro(args, e.args[0])
674 return
674 return
675 except InteractivelyDefined as e:
675 except InteractivelyDefined as e:
676 print("Editing In[%i]" % e.index)
676 print("Editing In[%i]" % e.index)
677 args = str(e.index)
677 args = str(e.index)
678 filename, lineno, is_temp = self._find_edit_target(self.shell,
678 filename, lineno, is_temp = self._find_edit_target(self.shell,
679 args, opts, last_call)
679 args, opts, last_call)
680 if filename is None:
680 if filename is None:
681 # nothing was found, warnings have already been issued,
681 # nothing was found, warnings have already been issued,
682 # just give up.
682 # just give up.
683 return
683 return
684
684
685 if is_temp:
685 if is_temp:
686 self._knowntemps.add(filename)
686 self._knowntemps.add(filename)
687 elif (filename in self._knowntemps):
687 elif (filename in self._knowntemps):
688 is_temp = True
688 is_temp = True
689
689
690
690
691 # do actual editing here
691 # do actual editing here
692 print('Editing...', end=' ')
692 print('Editing...', end=' ')
693 sys.stdout.flush()
693 sys.stdout.flush()
694 filepath = Path(filename)
694 filepath = Path(filename)
695 try:
695 try:
696 # Quote filenames that may have spaces in them when opening
696 # Quote filenames that may have spaces in them when opening
697 # the editor
697 # the editor
698 quoted = filename = str(filepath.absolute())
698 quoted = filename = str(filepath.absolute())
699 if " " in quoted:
699 if " " in quoted:
700 quoted = "'%s'" % quoted
700 quoted = "'%s'" % quoted
701 self.shell.hooks.editor(quoted, lineno)
701 self.shell.hooks.editor(quoted, lineno)
702 except TryNext:
702 except TryNext:
703 warn('Could not open editor')
703 warn('Could not open editor')
704 return
704 return
705
705
706 # XXX TODO: should this be generalized for all string vars?
706 # XXX TODO: should this be generalized for all string vars?
707 # For now, this is special-cased to blocks created by cpaste
707 # For now, this is special-cased to blocks created by cpaste
708 if args.strip() == "pasted_block":
708 if args.strip() == "pasted_block":
709 self.shell.user_ns["pasted_block"] = filepath.read_text()
709 self.shell.user_ns["pasted_block"] = filepath.read_text()
710
710
711 if 'x' in opts: # -x prevents actual execution
711 if 'x' in opts: # -x prevents actual execution
712 print()
712 print()
713 else:
713 else:
714 print('done. Executing edited code...')
714 print('done. Executing edited code...')
715 with preserve_keys(self.shell.user_ns, '__file__'):
715 with preserve_keys(self.shell.user_ns, '__file__'):
716 if not is_temp:
716 if not is_temp:
717 self.shell.user_ns['__file__'] = filename
717 self.shell.user_ns['__file__'] = filename
718 if 'r' in opts: # Untranslated IPython code
718 if 'r' in opts: # Untranslated IPython code
719 source = filepath.read_text()
719 source = filepath.read_text()
720 self.shell.run_cell(source, store_history=False)
720 self.shell.run_cell(source, store_history=False)
721 else:
721 else:
722 self.shell.safe_execfile(filename, self.shell.user_ns,
722 self.shell.safe_execfile(filename, self.shell.user_ns,
723 self.shell.user_ns)
723 self.shell.user_ns)
724
724
725 if is_temp:
725 if is_temp:
726 try:
726 try:
727 return filepath.read_text()
727 return filepath.read_text()
728 except IOError as msg:
728 except IOError as msg:
729 if Path(msg.filename) == filepath:
729 if Path(msg.filename) == filepath:
730 warn('File not found. Did you forget to save?')
730 warn('File not found. Did you forget to save?')
731 return
731 return
732 else:
732 else:
733 self.shell.showtraceback()
733 self.shell.showtraceback()
General Comments 0
You need to be logged in to leave comments. Login now