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