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