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