##// END OF EJS Templates
Load whole history on `%load` without parameters
Blazej Michalik -
Show More
@@ -1,743 +1,741 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 260 If no arguments are given, uploads the history of this session up to
261 261 this point.
262 262
263 263 Options:
264 264
265 265 -d: Pass a custom description. The default will say
266 266 "Pasted from IPython".
267 267 """
268 268 opts, args = self.parse_options(parameter_s, 'd:')
269 269
270 270 try:
271 271 code = self.shell.find_user_code(args)
272 272 except (ValueError, TypeError) as e:
273 273 print(e.args[0])
274 274 return
275 275
276 276 post_data = urlencode(
277 277 {
278 278 "title": opts.get("d", "Pasted from IPython"),
279 279 "syntax": "python",
280 280 "content": code,
281 281 }
282 282 ).encode("utf-8")
283 283
284 284 request = Request(
285 285 "http://dpaste.com/api/v2/",
286 286 headers={"User-Agent": "IPython v{}".format(version)},
287 287 )
288 288 response = urlopen(request, post_data)
289 289 return response.headers.get('Location')
290 290
291 291 @line_magic
292 292 def loadpy(self, arg_s):
293 293 """Alias of `%load`
294 294
295 295 `%loadpy` has gained some flexibility and dropped the requirement of a `.py`
296 296 extension. So it has been renamed simply into %load. You can look at
297 297 `%load`'s docstring for more info.
298 298 """
299 299 self.load(arg_s)
300 300
301 301 @line_magic
302 302 def load(self, arg_s):
303 303 """Load code into the current frontend.
304 304
305 305 Usage:\\
306 306 %load [options] source
307 307
308 308 where source can be a filename, URL, input history range, macro, or
309 309 element in the user namespace
310 310
311 If no arguments are given, loads the history of this session up to this
312 point.
313
311 314 Options:
312 315
313 316 -r <lines>: Specify lines or ranges of lines to load from the source.
314 317 Ranges could be specified as x-y (x..y) or in python-style x:y
315 318 (x..(y-1)). Both limits x and y can be left blank (meaning the
316 319 beginning and end of the file, respectively).
317 320
318 321 -s <symbols>: Specify function or classes to load from python source.
319 322
320 323 -y : Don't ask confirmation for loading source above 200 000 characters.
321 324
322 325 -n : Include the user's namespace when searching for source code.
323 326
324 327 This magic command can either take a local filename, a URL, an history
325 328 range (see %history) or a macro as argument, it will prompt for
326 329 confirmation before loading source with more than 200 000 characters, unless
327 330 -y flag is passed or if the frontend does not support raw_input::
328 331
332 %load
329 333 %load myscript.py
330 334 %load 7-27
331 335 %load myMacro
332 336 %load http://www.example.com/myscript.py
333 337 %load -r 5-10 myscript.py
334 338 %load -r 10-20,30,40: foo.py
335 339 %load -s MyClass,wonder_function myscript.py
336 340 %load -n MyClass
337 341 %load -n my_module.wonder_function
338 342 """
339 343 opts,args = self.parse_options(arg_s,'yns:r:')
340
341 if not args:
342 raise UsageError('Missing filename, URL, input history range, '
343 'macro, or element in the user namespace.')
344
345 344 search_ns = 'n' in opts
346
347 345 contents = self.shell.find_user_code(args, search_ns=search_ns)
348 346
349 347 if 's' in opts:
350 348 try:
351 349 blocks, not_found = extract_symbols(contents, opts['s'])
352 350 except SyntaxError:
353 351 # non python code
354 352 error("Unable to parse the input as valid Python code")
355 353 return
356 354
357 355 if len(not_found) == 1:
358 356 warn('The symbol `%s` was not found' % not_found[0])
359 357 elif len(not_found) > 1:
360 358 warn('The symbols %s were not found' % get_text_list(not_found,
361 359 wrap_item_with='`')
362 360 )
363 361
364 362 contents = '\n'.join(blocks)
365 363
366 364 if 'r' in opts:
367 365 ranges = opts['r'].replace(',', ' ')
368 366 lines = contents.split('\n')
369 367 slices = extract_code_ranges(ranges)
370 368 contents = [lines[slice(*slc)] for slc in slices]
371 369 contents = '\n'.join(strip_initial_indent(chain.from_iterable(contents)))
372 370
373 371 l = len(contents)
374 372
375 373 # 200 000 is ~ 2500 full 80 character lines
376 374 # so in average, more than 5000 lines
377 375 if l > 200000 and 'y' not in opts:
378 376 try:
379 377 ans = self.shell.ask_yes_no(("The text you're trying to load seems pretty big"\
380 378 " (%d characters). Continue (y/[N]) ?" % l), default='n' )
381 379 except StdinNotImplementedError:
382 380 #assume yes if raw input not implemented
383 381 ans = True
384 382
385 383 if ans is False :
386 384 print('Operation cancelled.')
387 385 return
388 386
389 387 contents = "# %load {}\n".format(arg_s) + contents
390 388
391 389 self.shell.set_next_input(contents, replace=True)
392 390
393 391 @staticmethod
394 392 def _find_edit_target(shell, args, opts, last_call):
395 393 """Utility method used by magic_edit to find what to edit."""
396 394
397 395 def make_filename(arg):
398 396 "Make a filename from the given args"
399 397 try:
400 398 filename = get_py_filename(arg)
401 399 except IOError:
402 400 # If it ends with .py but doesn't already exist, assume we want
403 401 # a new file.
404 402 if arg.endswith('.py'):
405 403 filename = arg
406 404 else:
407 405 filename = None
408 406 return filename
409 407
410 408 # Set a few locals from the options for convenience:
411 409 opts_prev = 'p' in opts
412 410 opts_raw = 'r' in opts
413 411
414 412 # custom exceptions
415 413 class DataIsObject(Exception): pass
416 414
417 415 # Default line number value
418 416 lineno = opts.get('n',None)
419 417
420 418 if opts_prev:
421 419 args = '_%s' % last_call[0]
422 420 if args not in shell.user_ns:
423 421 args = last_call[1]
424 422
425 423 # by default this is done with temp files, except when the given
426 424 # arg is a filename
427 425 use_temp = True
428 426
429 427 data = ''
430 428
431 429 # First, see if the arguments should be a filename.
432 430 filename = make_filename(args)
433 431 if filename:
434 432 use_temp = False
435 433 elif args:
436 434 # Mode where user specifies ranges of lines, like in %macro.
437 435 data = shell.extract_input_lines(args, opts_raw)
438 436 if not data:
439 437 try:
440 438 # Load the parameter given as a variable. If not a string,
441 439 # process it as an object instead (below)
442 440
443 441 #print '*** args',args,'type',type(args) # dbg
444 442 data = eval(args, shell.user_ns)
445 443 if not isinstance(data, str):
446 444 raise DataIsObject
447 445
448 446 except (NameError,SyntaxError):
449 447 # given argument is not a variable, try as a filename
450 448 filename = make_filename(args)
451 449 if filename is None:
452 450 warn("Argument given (%s) can't be found as a variable "
453 451 "or as a filename." % args)
454 452 return (None, None, None)
455 453 use_temp = False
456 454
457 455 except DataIsObject as e:
458 456 # macros have a special edit function
459 457 if isinstance(data, Macro):
460 458 raise MacroToEdit(data) from e
461 459
462 460 # For objects, try to edit the file where they are defined
463 461 filename = find_file(data)
464 462 if filename:
465 463 if 'fakemodule' in filename.lower() and \
466 464 inspect.isclass(data):
467 465 # class created by %edit? Try to find source
468 466 # by looking for method definitions instead, the
469 467 # __module__ in those classes is FakeModule.
470 468 attrs = [getattr(data, aname) for aname in dir(data)]
471 469 for attr in attrs:
472 470 if not inspect.ismethod(attr):
473 471 continue
474 472 filename = find_file(attr)
475 473 if filename and \
476 474 'fakemodule' not in filename.lower():
477 475 # change the attribute to be the edit
478 476 # target instead
479 477 data = attr
480 478 break
481 479
482 480 m = ipython_input_pat.match(os.path.basename(filename))
483 481 if m:
484 482 raise InteractivelyDefined(int(m.groups()[0])) from e
485 483
486 484 datafile = 1
487 485 if filename is None:
488 486 filename = make_filename(args)
489 487 datafile = 1
490 488 if filename is not None:
491 489 # only warn about this if we get a real name
492 490 warn('Could not find file where `%s` is defined.\n'
493 491 'Opening a file named `%s`' % (args, filename))
494 492 # Now, make sure we can actually read the source (if it was
495 493 # in a temp file it's gone by now).
496 494 if datafile:
497 495 if lineno is None:
498 496 lineno = find_source_lines(data)
499 497 if lineno is None:
500 498 filename = make_filename(args)
501 499 if filename is None:
502 500 warn('The file where `%s` was defined '
503 501 'cannot be read or found.' % data)
504 502 return (None, None, None)
505 503 use_temp = False
506 504
507 505 if use_temp:
508 506 filename = shell.mktempfile(data)
509 507 print('IPython will make a temporary file named:',filename)
510 508
511 509 # use last_call to remember the state of the previous call, but don't
512 510 # let it be clobbered by successive '-p' calls.
513 511 try:
514 512 last_call[0] = shell.displayhook.prompt_count
515 513 if not opts_prev:
516 514 last_call[1] = args
517 515 except:
518 516 pass
519 517
520 518
521 519 return filename, lineno, use_temp
522 520
523 521 def _edit_macro(self,mname,macro):
524 522 """open an editor with the macro data in a file"""
525 523 filename = self.shell.mktempfile(macro.value)
526 524 self.shell.hooks.editor(filename)
527 525
528 526 # and make a new macro object, to replace the old one
529 527 mvalue = Path(filename).read_text()
530 528 self.shell.user_ns[mname] = Macro(mvalue)
531 529
532 530 @skip_doctest
533 531 @line_magic
534 532 def edit(self, parameter_s='',last_call=['','']):
535 533 """Bring up an editor and execute the resulting code.
536 534
537 535 Usage:
538 536 %edit [options] [args]
539 537
540 538 %edit runs IPython's editor hook. The default version of this hook is
541 539 set to call the editor specified by your $EDITOR environment variable.
542 540 If this isn't found, it will default to vi under Linux/Unix and to
543 541 notepad under Windows. See the end of this docstring for how to change
544 542 the editor hook.
545 543
546 544 You can also set the value of this editor via the
547 545 ``TerminalInteractiveShell.editor`` option in your configuration file.
548 546 This is useful if you wish to use a different editor from your typical
549 547 default with IPython (and for Windows users who typically don't set
550 548 environment variables).
551 549
552 550 This command allows you to conveniently edit multi-line code right in
553 551 your IPython session.
554 552
555 553 If called without arguments, %edit opens up an empty editor with a
556 554 temporary file and will execute the contents of this file when you
557 555 close it (don't forget to save it!).
558 556
559 557
560 558 Options:
561 559
562 560 -n <number>: open the editor at a specified line number. By default,
563 561 the IPython editor hook uses the unix syntax 'editor +N filename', but
564 562 you can configure this by providing your own modified hook if your
565 563 favorite editor supports line-number specifications with a different
566 564 syntax.
567 565
568 566 -p: this will call the editor with the same data as the previous time
569 567 it was used, regardless of how long ago (in your current session) it
570 568 was.
571 569
572 570 -r: use 'raw' input. This option only applies to input taken from the
573 571 user's history. By default, the 'processed' history is used, so that
574 572 magics are loaded in their transformed version to valid Python. If
575 573 this option is given, the raw input as typed as the command line is
576 574 used instead. When you exit the editor, it will be executed by
577 575 IPython's own processor.
578 576
579 577 -x: do not execute the edited code immediately upon exit. This is
580 578 mainly useful if you are editing programs which need to be called with
581 579 command line arguments, which you can then do using %run.
582 580
583 581
584 582 Arguments:
585 583
586 584 If arguments are given, the following possibilities exist:
587 585
588 586 - If the argument is a filename, IPython will load that into the
589 587 editor. It will execute its contents with execfile() when you exit,
590 588 loading any code in the file into your interactive namespace.
591 589
592 590 - The arguments are ranges of input history, e.g. "7 ~1/4-6".
593 591 The syntax is the same as in the %history magic.
594 592
595 593 - If the argument is a string variable, its contents are loaded
596 594 into the editor. You can thus edit any string which contains
597 595 python code (including the result of previous edits).
598 596
599 597 - If the argument is the name of an object (other than a string),
600 598 IPython will try to locate the file where it was defined and open the
601 599 editor at the point where it is defined. You can use `%edit function`
602 600 to load an editor exactly at the point where 'function' is defined,
603 601 edit it and have the file be executed automatically.
604 602
605 603 - If the object is a macro (see %macro for details), this opens up your
606 604 specified editor with a temporary file containing the macro's data.
607 605 Upon exit, the macro is reloaded with the contents of the file.
608 606
609 607 Note: opening at an exact line is only supported under Unix, and some
610 608 editors (like kedit and gedit up to Gnome 2.8) do not understand the
611 609 '+NUMBER' parameter necessary for this feature. Good editors like
612 610 (X)Emacs, vi, jed, pico and joe all do.
613 611
614 612 After executing your code, %edit will return as output the code you
615 613 typed in the editor (except when it was an existing file). This way
616 614 you can reload the code in further invocations of %edit as a variable,
617 615 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
618 616 the output.
619 617
620 618 Note that %edit is also available through the alias %ed.
621 619
622 620 This is an example of creating a simple function inside the editor and
623 621 then modifying it. First, start up the editor::
624 622
625 623 In [1]: edit
626 624 Editing... done. Executing edited code...
627 625 Out[1]: 'def foo():\\n print "foo() was defined in an editing
628 626 session"\\n'
629 627
630 628 We can then call the function foo()::
631 629
632 630 In [2]: foo()
633 631 foo() was defined in an editing session
634 632
635 633 Now we edit foo. IPython automatically loads the editor with the
636 634 (temporary) file where foo() was previously defined::
637 635
638 636 In [3]: edit foo
639 637 Editing... done. Executing edited code...
640 638
641 639 And if we call foo() again we get the modified version::
642 640
643 641 In [4]: foo()
644 642 foo() has now been changed!
645 643
646 644 Here is an example of how to edit a code snippet successive
647 645 times. First we call the editor::
648 646
649 647 In [5]: edit
650 648 Editing... done. Executing edited code...
651 649 hello
652 650 Out[5]: "print 'hello'\\n"
653 651
654 652 Now we call it again with the previous output (stored in _)::
655 653
656 654 In [6]: edit _
657 655 Editing... done. Executing edited code...
658 656 hello world
659 657 Out[6]: "print 'hello world'\\n"
660 658
661 659 Now we call it with the output #8 (stored in _8, also as Out[8])::
662 660
663 661 In [7]: edit _8
664 662 Editing... done. Executing edited code...
665 663 hello again
666 664 Out[7]: "print 'hello again'\\n"
667 665
668 666
669 667 Changing the default editor hook:
670 668
671 669 If you wish to write your own editor hook, you can put it in a
672 670 configuration file which you load at startup time. The default hook
673 671 is defined in the IPython.core.hooks module, and you can use that as a
674 672 starting example for further modifications. That file also has
675 673 general instructions on how to set a new hook for use once you've
676 674 defined it."""
677 675 opts,args = self.parse_options(parameter_s,'prxn:')
678 676
679 677 try:
680 678 filename, lineno, is_temp = self._find_edit_target(self.shell,
681 679 args, opts, last_call)
682 680 except MacroToEdit as e:
683 681 self._edit_macro(args, e.args[0])
684 682 return
685 683 except InteractivelyDefined as e:
686 684 print("Editing In[%i]" % e.index)
687 685 args = str(e.index)
688 686 filename, lineno, is_temp = self._find_edit_target(self.shell,
689 687 args, opts, last_call)
690 688 if filename is None:
691 689 # nothing was found, warnings have already been issued,
692 690 # just give up.
693 691 return
694 692
695 693 if is_temp:
696 694 self._knowntemps.add(filename)
697 695 elif (filename in self._knowntemps):
698 696 is_temp = True
699 697
700 698
701 699 # do actual editing here
702 700 print('Editing...', end=' ')
703 701 sys.stdout.flush()
704 702 filepath = Path(filename)
705 703 try:
706 704 # Quote filenames that may have spaces in them when opening
707 705 # the editor
708 706 quoted = filename = str(filepath.absolute())
709 707 if " " in quoted:
710 708 quoted = "'%s'" % quoted
711 709 self.shell.hooks.editor(quoted, lineno)
712 710 except TryNext:
713 711 warn('Could not open editor')
714 712 return
715 713
716 714 # XXX TODO: should this be generalized for all string vars?
717 715 # For now, this is special-cased to blocks created by cpaste
718 716 if args.strip() == "pasted_block":
719 717 self.shell.user_ns["pasted_block"] = filepath.read_text()
720 718
721 719 if 'x' in opts: # -x prevents actual execution
722 720 print()
723 721 else:
724 722 print('done. Executing edited code...')
725 723 with preserve_keys(self.shell.user_ns, '__file__'):
726 724 if not is_temp:
727 725 self.shell.user_ns['__file__'] = filename
728 726 if 'r' in opts: # Untranslated IPython code
729 727 source = filepath.read_text()
730 728 self.shell.run_cell(source, store_history=False)
731 729 else:
732 730 self.shell.safe_execfile(filename, self.shell.user_ns,
733 731 self.shell.user_ns)
734 732
735 733 if is_temp:
736 734 try:
737 735 return filepath.read_text()
738 736 except IOError as msg:
739 737 if Path(msg.filename) == filepath:
740 738 warn('File not found. Did you forget to save?')
741 739 return
742 740 else:
743 741 self.shell.showtraceback()
General Comments 0
You need to be logged in to leave comments. Login now