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