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