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