##// END OF EJS Templates
Use oinspect in CodeMagics._find_edit_target...
Takafumi Arakaki -
Show More
@@ -1,513 +1,513 b''
1 1 """Implementation of code management magic functions.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2012 The IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 # Stdlib
16 16 import inspect
17 17 import io
18 18 import json
19 19 import os
20 20 import sys
21 21 from urllib2 import urlopen
22 22
23 23 # Our own packages
24 24 from IPython.core.error import TryNext
25 25 from IPython.core.macro import Macro
26 26 from IPython.core.magic import Magics, magics_class, line_magic
27 from IPython.core.oinspect import find_file, find_source_lines
27 28 from IPython.testing.skipdoctest import skip_doctest
28 29 from IPython.utils import openpy
29 30 from IPython.utils import py3compat
30 31 from IPython.utils.io import file_read
31 32 from IPython.utils.path import get_py_filename, unquote_filename
32 33 from IPython.utils.warn import warn
33 34
34 35 #-----------------------------------------------------------------------------
35 36 # Magic implementation classes
36 37 #-----------------------------------------------------------------------------
37 38
38 39 # Used for exception handling in magic_edit
39 40 class MacroToEdit(ValueError): pass
40 41
41 42
42 43 @magics_class
43 44 class CodeMagics(Magics):
44 45 """Magics related to code management (loading, saving, editing, ...)."""
45 46
46 47 @line_magic
47 48 def save(self, parameter_s=''):
48 49 """Save a set of lines or a macro to a given filename.
49 50
50 51 Usage:\\
51 52 %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ...
52 53
53 54 Options:
54 55
55 56 -r: use 'raw' input. By default, the 'processed' history is used,
56 57 so that magics are loaded in their transformed version to valid
57 58 Python. If this option is given, the raw input as typed as the
58 59 command line is used instead.
59 60
60 61 This function uses the same syntax as %history for input ranges,
61 62 then saves the lines to the filename you specify.
62 63
63 64 It adds a '.py' extension to the file if you don't do so yourself, and
64 65 it asks for confirmation before overwriting existing files.
65 66
66 67 If `-r` option is used, the default extension is `.ipy`.
67 68 """
68 69
69 70 opts,args = self.parse_options(parameter_s,'r',mode='list')
70 71 raw = 'r' in opts
71 72 ext = u'.ipy' if raw else u'.py'
72 73 fname, codefrom = unquote_filename(args[0]), " ".join(args[1:])
73 74 if not fname.endswith((u'.py',u'.ipy')):
74 75 fname += ext
75 76 if os.path.isfile(fname):
76 77 overwrite = self.shell.ask_yes_no('File `%s` exists. Overwrite (y/[N])? ' % fname, default='n')
77 78 if not overwrite :
78 79 print 'Operation cancelled.'
79 80 return
80 81 try:
81 82 cmds = self.shell.find_user_code(codefrom,raw)
82 83 except (TypeError, ValueError) as e:
83 84 print e.args[0]
84 85 return
85 86 with io.open(fname,'w', encoding="utf-8") as f:
86 87 f.write(u"# coding: utf-8\n")
87 88 f.write(py3compat.cast_unicode(cmds))
88 89 print 'The following commands were written to file `%s`:' % fname
89 90 print cmds
90 91
91 92 @line_magic
92 93 def pastebin(self, parameter_s=''):
93 94 """Upload code to Github's Gist paste bin, returning the URL.
94 95
95 96 Usage:\\
96 97 %pastebin [-d "Custom description"] 1-7
97 98
98 99 The argument can be an input history range, a filename, or the name of a
99 100 string or macro.
100 101
101 102 Options:
102 103
103 104 -d: Pass a custom description for the gist. The default will say
104 105 "Pasted from IPython".
105 106 """
106 107 opts, args = self.parse_options(parameter_s, 'd:')
107 108
108 109 try:
109 110 code = self.shell.find_user_code(args)
110 111 except (ValueError, TypeError) as e:
111 112 print e.args[0]
112 113 return
113 114
114 115 post_data = json.dumps({
115 116 "description": opts.get('d', "Pasted from IPython"),
116 117 "public": True,
117 118 "files": {
118 119 "file1.py": {
119 120 "content": code
120 121 }
121 122 }
122 123 }).encode('utf-8')
123 124
124 125 response = urlopen("https://api.github.com/gists", post_data)
125 126 response_data = json.loads(response.read().decode('utf-8'))
126 127 return response_data['html_url']
127 128
128 129 @line_magic
129 130 def loadpy(self, arg_s):
130 131 """Alias of `%load`
131 132
132 133 `%loadpy` has gained some flexibility and droped the requirement of a `.py`
133 134 extension. So it has been renamed simply into %load. You can look at
134 135 `%load`'s docstring for more info.
135 136 """
136 137 self.load(arg_s)
137 138
138 139 @line_magic
139 140 def load(self, arg_s):
140 141 """Load code into the current frontend.
141 142
142 143 Usage:\\
143 144 %load [options] source
144 145
145 146 where source can be a filename, URL, input history range or macro
146 147
147 148 Options:
148 149 --------
149 150 -y : Don't ask confirmation for loading source above 200 000 characters.
150 151
151 152 This magic command can either take a local filename, a URL, an history
152 153 range (see %history) or a macro as argument, it will prompt for
153 154 confirmation before loading source with more than 200 000 characters, unless
154 155 -y flag is passed or if the frontend does not support raw_input::
155 156
156 157 %load myscript.py
157 158 %load 7-27
158 159 %load myMacro
159 160 %load http://www.example.com/myscript.py
160 161 """
161 162 opts,args = self.parse_options(arg_s,'y')
162 163
163 164 contents = self.shell.find_user_code(args)
164 165 l = len(contents)
165 166
166 167 # 200 000 is ~ 2500 full 80 caracter lines
167 168 # so in average, more than 5000 lines
168 169 if l > 200000 and 'y' not in opts:
169 170 try:
170 171 ans = self.shell.ask_yes_no(("The text you're trying to load seems pretty big"\
171 172 " (%d characters). Continue (y/[N]) ?" % l), default='n' )
172 173 except StdinNotImplementedError:
173 174 #asume yes if raw input not implemented
174 175 ans = True
175 176
176 177 if ans is False :
177 178 print 'Operation cancelled.'
178 179 return
179 180
180 181 self.shell.set_next_input(contents)
181 182
182 183 @staticmethod
183 184 def _find_edit_target(shell, args, opts, last_call):
184 185 """Utility method used by magic_edit to find what to edit."""
185 186
186 187 def make_filename(arg):
187 188 "Make a filename from the given args"
188 189 arg = unquote_filename(arg)
189 190 try:
190 191 filename = get_py_filename(arg)
191 192 except IOError:
192 193 # If it ends with .py but doesn't already exist, assume we want
193 194 # a new file.
194 195 if arg.endswith('.py'):
195 196 filename = arg
196 197 else:
197 198 filename = None
198 199 return filename
199 200
200 201 # Set a few locals from the options for convenience:
201 202 opts_prev = 'p' in opts
202 203 opts_raw = 'r' in opts
203 204
204 205 # custom exceptions
205 206 class DataIsObject(Exception): pass
206 207
207 208 # Default line number value
208 209 lineno = opts.get('n',None)
209 210
210 211 if opts_prev:
211 212 args = '_%s' % last_call[0]
212 213 if not shell.user_ns.has_key(args):
213 214 args = last_call[1]
214 215
215 216 # use last_call to remember the state of the previous call, but don't
216 217 # let it be clobbered by successive '-p' calls.
217 218 try:
218 219 last_call[0] = shell.displayhook.prompt_count
219 220 if not opts_prev:
220 221 last_call[1] = args
221 222 except:
222 223 pass
223 224
224 225 # by default this is done with temp files, except when the given
225 226 # arg is a filename
226 227 use_temp = True
227 228
228 229 data = ''
229 230
230 231 # First, see if the arguments should be a filename.
231 232 filename = make_filename(args)
232 233 if filename:
233 234 use_temp = False
234 235 elif args:
235 236 # Mode where user specifies ranges of lines, like in %macro.
236 237 data = shell.extract_input_lines(args, opts_raw)
237 238 if not data:
238 239 try:
239 240 # Load the parameter given as a variable. If not a string,
240 241 # process it as an object instead (below)
241 242
242 243 #print '*** args',args,'type',type(args) # dbg
243 244 data = eval(args, shell.user_ns)
244 245 if not isinstance(data, basestring):
245 246 raise DataIsObject
246 247
247 248 except (NameError,SyntaxError):
248 249 # given argument is not a variable, try as a filename
249 250 filename = make_filename(args)
250 251 if filename is None:
251 252 warn("Argument given (%s) can't be found as a variable "
252 253 "or as a filename." % args)
253 254 return
254 255 use_temp = False
255 256
256 257 except DataIsObject:
257 258 # macros have a special edit function
258 259 if isinstance(data, Macro):
259 260 raise MacroToEdit(data)
260 261
261 262 # For objects, try to edit the file where they are defined
262 try:
263 filename = inspect.getabsfile(data)
263 filename = find_file(data)
264 if filename:
264 265 if 'fakemodule' in filename.lower() and \
265 266 inspect.isclass(data):
266 267 # class created by %edit? Try to find source
267 268 # by looking for method definitions instead, the
268 269 # __module__ in those classes is FakeModule.
269 270 attrs = [getattr(data, aname) for aname in dir(data)]
270 271 for attr in attrs:
271 272 if not inspect.ismethod(attr):
272 273 continue
273 filename = inspect.getabsfile(attr)
274 filename = find_file(attr)
274 275 if filename and \
275 276 'fakemodule' not in filename.lower():
276 277 # change the attribute to be the edit
277 278 # target instead
278 279 data = attr
279 280 break
280 281
281 282 datafile = 1
282 except TypeError:
283 if filename is None:
283 284 filename = make_filename(args)
284 285 datafile = 1
285 286 warn('Could not find file where `%s` is defined.\n'
286 287 'Opening a file named `%s`' % (args, filename))
287 288 # Now, make sure we can actually read the source (if it was
288 289 # in a temp file it's gone by now).
289 290 if datafile:
290 try:
291 if lineno is None:
292 lineno = inspect.getsourcelines(data)[1]
293 except IOError:
291 if lineno is None:
292 lineno = find_source_lines(data)
293 if lineno is None:
294 294 filename = make_filename(args)
295 295 if filename is None:
296 296 warn('The file `%s` where `%s` was defined '
297 297 'cannot be read.' % (filename, data))
298 298 return
299 299 use_temp = False
300 300
301 301 if use_temp:
302 302 filename = shell.mktempfile(data)
303 303 print 'IPython will make a temporary file named:',filename
304 304
305 305 return filename, lineno, use_temp
306 306
307 307 def _edit_macro(self,mname,macro):
308 308 """open an editor with the macro data in a file"""
309 309 filename = self.shell.mktempfile(macro.value)
310 310 self.shell.hooks.editor(filename)
311 311
312 312 # and make a new macro object, to replace the old one
313 313 mfile = open(filename)
314 314 mvalue = mfile.read()
315 315 mfile.close()
316 316 self.shell.user_ns[mname] = Macro(mvalue)
317 317
318 318 @line_magic
319 319 def ed(self, parameter_s=''):
320 320 """Alias to %edit."""
321 321 return self.edit(parameter_s)
322 322
323 323 @skip_doctest
324 324 @line_magic
325 325 def edit(self, parameter_s='',last_call=['','']):
326 326 """Bring up an editor and execute the resulting code.
327 327
328 328 Usage:
329 329 %edit [options] [args]
330 330
331 331 %edit runs IPython's editor hook. The default version of this hook is
332 332 set to call the editor specified by your $EDITOR environment variable.
333 333 If this isn't found, it will default to vi under Linux/Unix and to
334 334 notepad under Windows. See the end of this docstring for how to change
335 335 the editor hook.
336 336
337 337 You can also set the value of this editor via the
338 338 ``TerminalInteractiveShell.editor`` option in your configuration file.
339 339 This is useful if you wish to use a different editor from your typical
340 340 default with IPython (and for Windows users who typically don't set
341 341 environment variables).
342 342
343 343 This command allows you to conveniently edit multi-line code right in
344 344 your IPython session.
345 345
346 346 If called without arguments, %edit opens up an empty editor with a
347 347 temporary file and will execute the contents of this file when you
348 348 close it (don't forget to save it!).
349 349
350 350
351 351 Options:
352 352
353 353 -n <number>: open the editor at a specified line number. By default,
354 354 the IPython editor hook uses the unix syntax 'editor +N filename', but
355 355 you can configure this by providing your own modified hook if your
356 356 favorite editor supports line-number specifications with a different
357 357 syntax.
358 358
359 359 -p: this will call the editor with the same data as the previous time
360 360 it was used, regardless of how long ago (in your current session) it
361 361 was.
362 362
363 363 -r: use 'raw' input. This option only applies to input taken from the
364 364 user's history. By default, the 'processed' history is used, so that
365 365 magics are loaded in their transformed version to valid Python. If
366 366 this option is given, the raw input as typed as the command line is
367 367 used instead. When you exit the editor, it will be executed by
368 368 IPython's own processor.
369 369
370 370 -x: do not execute the edited code immediately upon exit. This is
371 371 mainly useful if you are editing programs which need to be called with
372 372 command line arguments, which you can then do using %run.
373 373
374 374
375 375 Arguments:
376 376
377 377 If arguments are given, the following possibilities exist:
378 378
379 379 - If the argument is a filename, IPython will load that into the
380 380 editor. It will execute its contents with execfile() when you exit,
381 381 loading any code in the file into your interactive namespace.
382 382
383 383 - The arguments are ranges of input history, e.g. "7 ~1/4-6".
384 384 The syntax is the same as in the %history magic.
385 385
386 386 - If the argument is a string variable, its contents are loaded
387 387 into the editor. You can thus edit any string which contains
388 388 python code (including the result of previous edits).
389 389
390 390 - If the argument is the name of an object (other than a string),
391 391 IPython will try to locate the file where it was defined and open the
392 392 editor at the point where it is defined. You can use `%edit function`
393 393 to load an editor exactly at the point where 'function' is defined,
394 394 edit it and have the file be executed automatically.
395 395
396 396 - If the object is a macro (see %macro for details), this opens up your
397 397 specified editor with a temporary file containing the macro's data.
398 398 Upon exit, the macro is reloaded with the contents of the file.
399 399
400 400 Note: opening at an exact line is only supported under Unix, and some
401 401 editors (like kedit and gedit up to Gnome 2.8) do not understand the
402 402 '+NUMBER' parameter necessary for this feature. Good editors like
403 403 (X)Emacs, vi, jed, pico and joe all do.
404 404
405 405 After executing your code, %edit will return as output the code you
406 406 typed in the editor (except when it was an existing file). This way
407 407 you can reload the code in further invocations of %edit as a variable,
408 408 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
409 409 the output.
410 410
411 411 Note that %edit is also available through the alias %ed.
412 412
413 413 This is an example of creating a simple function inside the editor and
414 414 then modifying it. First, start up the editor::
415 415
416 416 In [1]: ed
417 417 Editing... done. Executing edited code...
418 418 Out[1]: 'def foo():\\n print "foo() was defined in an editing
419 419 session"\\n'
420 420
421 421 We can then call the function foo()::
422 422
423 423 In [2]: foo()
424 424 foo() was defined in an editing session
425 425
426 426 Now we edit foo. IPython automatically loads the editor with the
427 427 (temporary) file where foo() was previously defined::
428 428
429 429 In [3]: ed foo
430 430 Editing... done. Executing edited code...
431 431
432 432 And if we call foo() again we get the modified version::
433 433
434 434 In [4]: foo()
435 435 foo() has now been changed!
436 436
437 437 Here is an example of how to edit a code snippet successive
438 438 times. First we call the editor::
439 439
440 440 In [5]: ed
441 441 Editing... done. Executing edited code...
442 442 hello
443 443 Out[5]: "print 'hello'\\n"
444 444
445 445 Now we call it again with the previous output (stored in _)::
446 446
447 447 In [6]: ed _
448 448 Editing... done. Executing edited code...
449 449 hello world
450 450 Out[6]: "print 'hello world'\\n"
451 451
452 452 Now we call it with the output #8 (stored in _8, also as Out[8])::
453 453
454 454 In [7]: ed _8
455 455 Editing... done. Executing edited code...
456 456 hello again
457 457 Out[7]: "print 'hello again'\\n"
458 458
459 459
460 460 Changing the default editor hook:
461 461
462 462 If you wish to write your own editor hook, you can put it in a
463 463 configuration file which you load at startup time. The default hook
464 464 is defined in the IPython.core.hooks module, and you can use that as a
465 465 starting example for further modifications. That file also has
466 466 general instructions on how to set a new hook for use once you've
467 467 defined it."""
468 468 opts,args = self.parse_options(parameter_s,'prxn:')
469 469
470 470 try:
471 471 filename, lineno, is_temp = self._find_edit_target(self.shell,
472 472 args, opts, last_call)
473 473 except MacroToEdit as e:
474 474 self._edit_macro(args, e.args[0])
475 475 return
476 476
477 477 # do actual editing here
478 478 print 'Editing...',
479 479 sys.stdout.flush()
480 480 try:
481 481 # Quote filenames that may have spaces in them
482 482 if ' ' in filename:
483 483 filename = "'%s'" % filename
484 484 self.shell.hooks.editor(filename,lineno)
485 485 except TryNext:
486 486 warn('Could not open editor')
487 487 return
488 488
489 489 # XXX TODO: should this be generalized for all string vars?
490 490 # For now, this is special-cased to blocks created by cpaste
491 491 if args.strip() == 'pasted_block':
492 492 self.shell.user_ns['pasted_block'] = file_read(filename)
493 493
494 494 if 'x' in opts: # -x prevents actual execution
495 495 print
496 496 else:
497 497 print 'done. Executing edited code...'
498 498 if 'r' in opts: # Untranslated IPython code
499 499 self.shell.run_cell(file_read(filename),
500 500 store_history=False)
501 501 else:
502 502 self.shell.safe_execfile(filename, self.shell.user_ns,
503 503 self.shell.user_ns)
504 504
505 505 if is_temp:
506 506 try:
507 507 return open(filename).read()
508 508 except IOError,msg:
509 509 if msg.filename == filename:
510 510 warn('File not found. Did you forget to save?')
511 511 return
512 512 else:
513 513 self.shell.showtraceback()
General Comments 0
You need to be logged in to leave comments. Login now