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