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