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