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