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