##// END OF EJS Templates
Apply suggestions from code review...
Matthias Bussonnier -
Show More
@@ -1,734 +1,732 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 Path(filename).open() as mfile:
517 mvalue = mfile.read()
516 mvalue = Path(filename).read_text()
518 517 self.shell.user_ns[mname] = Macro(mvalue)
519 518
520 519 @skip_doctest
521 520 @line_magic
522 521 def edit(self, parameter_s='',last_call=['','']):
523 522 """Bring up an editor and execute the resulting code.
524 523
525 524 Usage:
526 525 %edit [options] [args]
527 526
528 527 %edit runs IPython's editor hook. The default version of this hook is
529 528 set to call the editor specified by your $EDITOR environment variable.
530 529 If this isn't found, it will default to vi under Linux/Unix and to
531 530 notepad under Windows. See the end of this docstring for how to change
532 531 the editor hook.
533 532
534 533 You can also set the value of this editor via the
535 534 ``TerminalInteractiveShell.editor`` option in your configuration file.
536 535 This is useful if you wish to use a different editor from your typical
537 536 default with IPython (and for Windows users who typically don't set
538 537 environment variables).
539 538
540 539 This command allows you to conveniently edit multi-line code right in
541 540 your IPython session.
542 541
543 542 If called without arguments, %edit opens up an empty editor with a
544 543 temporary file and will execute the contents of this file when you
545 544 close it (don't forget to save it!).
546 545
547 546
548 547 Options:
549 548
550 549 -n <number>: open the editor at a specified line number. By default,
551 550 the IPython editor hook uses the unix syntax 'editor +N filename', but
552 551 you can configure this by providing your own modified hook if your
553 552 favorite editor supports line-number specifications with a different
554 553 syntax.
555 554
556 555 -p: this will call the editor with the same data as the previous time
557 556 it was used, regardless of how long ago (in your current session) it
558 557 was.
559 558
560 559 -r: use 'raw' input. This option only applies to input taken from the
561 560 user's history. By default, the 'processed' history is used, so that
562 561 magics are loaded in their transformed version to valid Python. If
563 562 this option is given, the raw input as typed as the command line is
564 563 used instead. When you exit the editor, it will be executed by
565 564 IPython's own processor.
566 565
567 566 -x: do not execute the edited code immediately upon exit. This is
568 567 mainly useful if you are editing programs which need to be called with
569 568 command line arguments, which you can then do using %run.
570 569
571 570
572 571 Arguments:
573 572
574 573 If arguments are given, the following possibilities exist:
575 574
576 575 - If the argument is a filename, IPython will load that into the
577 576 editor. It will execute its contents with execfile() when you exit,
578 577 loading any code in the file into your interactive namespace.
579 578
580 579 - The arguments are ranges of input history, e.g. "7 ~1/4-6".
581 580 The syntax is the same as in the %history magic.
582 581
583 582 - If the argument is a string variable, its contents are loaded
584 583 into the editor. You can thus edit any string which contains
585 584 python code (including the result of previous edits).
586 585
587 586 - If the argument is the name of an object (other than a string),
588 587 IPython will try to locate the file where it was defined and open the
589 588 editor at the point where it is defined. You can use `%edit function`
590 589 to load an editor exactly at the point where 'function' is defined,
591 590 edit it and have the file be executed automatically.
592 591
593 592 - If the object is a macro (see %macro for details), this opens up your
594 593 specified editor with a temporary file containing the macro's data.
595 594 Upon exit, the macro is reloaded with the contents of the file.
596 595
597 596 Note: opening at an exact line is only supported under Unix, and some
598 597 editors (like kedit and gedit up to Gnome 2.8) do not understand the
599 598 '+NUMBER' parameter necessary for this feature. Good editors like
600 599 (X)Emacs, vi, jed, pico and joe all do.
601 600
602 601 After executing your code, %edit will return as output the code you
603 602 typed in the editor (except when it was an existing file). This way
604 603 you can reload the code in further invocations of %edit as a variable,
605 604 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
606 605 the output.
607 606
608 607 Note that %edit is also available through the alias %ed.
609 608
610 609 This is an example of creating a simple function inside the editor and
611 610 then modifying it. First, start up the editor::
612 611
613 612 In [1]: edit
614 613 Editing... done. Executing edited code...
615 614 Out[1]: 'def foo():\\n print "foo() was defined in an editing
616 615 session"\\n'
617 616
618 617 We can then call the function foo()::
619 618
620 619 In [2]: foo()
621 620 foo() was defined in an editing session
622 621
623 622 Now we edit foo. IPython automatically loads the editor with the
624 623 (temporary) file where foo() was previously defined::
625 624
626 625 In [3]: edit foo
627 626 Editing... done. Executing edited code...
628 627
629 628 And if we call foo() again we get the modified version::
630 629
631 630 In [4]: foo()
632 631 foo() has now been changed!
633 632
634 633 Here is an example of how to edit a code snippet successive
635 634 times. First we call the editor::
636 635
637 636 In [5]: edit
638 637 Editing... done. Executing edited code...
639 638 hello
640 639 Out[5]: "print 'hello'\\n"
641 640
642 641 Now we call it again with the previous output (stored in _)::
643 642
644 643 In [6]: edit _
645 644 Editing... done. Executing edited code...
646 645 hello world
647 646 Out[6]: "print 'hello world'\\n"
648 647
649 648 Now we call it with the output #8 (stored in _8, also as Out[8])::
650 649
651 650 In [7]: edit _8
652 651 Editing... done. Executing edited code...
653 652 hello again
654 653 Out[7]: "print 'hello again'\\n"
655 654
656 655
657 656 Changing the default editor hook:
658 657
659 658 If you wish to write your own editor hook, you can put it in a
660 659 configuration file which you load at startup time. The default hook
661 660 is defined in the IPython.core.hooks module, and you can use that as a
662 661 starting example for further modifications. That file also has
663 662 general instructions on how to set a new hook for use once you've
664 663 defined it."""
665 664 opts,args = self.parse_options(parameter_s,'prxn:')
666 665
667 666 try:
668 667 filename, lineno, is_temp = self._find_edit_target(self.shell,
669 668 args, opts, last_call)
670 669 except MacroToEdit as e:
671 670 self._edit_macro(args, e.args[0])
672 671 return
673 672 except InteractivelyDefined as e:
674 673 print("Editing In[%i]" % e.index)
675 674 args = str(e.index)
676 675 filename, lineno, is_temp = self._find_edit_target(self.shell,
677 676 args, opts, last_call)
678 677 if filename is None:
679 678 # nothing was found, warnings have already been issued,
680 679 # just give up.
681 680 return
682 681
683 682 if is_temp:
684 683 self._knowntemps.add(filename)
685 684 elif (filename in self._knowntemps):
686 685 is_temp = True
687 686
688 687
689 688 # do actual editing here
690 689 print('Editing...', end=' ')
691 690 sys.stdout.flush()
692 691 filepath = Path(filename)
693 692 try:
694 693 # Quote filenames that may have spaces in them when opening
695 694 # the editor
696 695 quoted = filename = str(filepath.absolute())
697 696 if ' ' in quoted:
698 697 quoted = "'%s'" % quoted
699 698 self.shell.hooks.editor(quoted,lineno)
700 699 except TryNext:
701 700 warn('Could not open editor')
702 701 return
703 702
704 703 # XXX TODO: should this be generalized for all string vars?
705 704 # For now, this is special-cased to blocks created by cpaste
706 705 if args.strip() == 'pasted_block':
707 with filepath.open('r') as f:
708 self.shell.user_ns['pasted_block'] = f.read()
706 self.shell.user_ns['pasted_block'] = filepath.read_text()
709 707
710 708 if 'x' in opts: # -x prevents actual execution
711 709 print()
712 710 else:
713 711 print('done. Executing edited code...')
714 712 with preserve_keys(self.shell.user_ns, '__file__'):
715 713 if not is_temp:
716 714 self.shell.user_ns['__file__'] = filename
717 715 if 'r' in opts: # Untranslated IPython code
718 716 with filepath.open('r') as f:
719 717 source = f.read()
720 718 self.shell.run_cell(source, store_history=False)
721 719 else:
722 720 self.shell.safe_execfile(filename, self.shell.user_ns,
723 721 self.shell.user_ns)
724 722
725 723 if is_temp:
726 724 try:
727 725 with filepath.open() as f:
728 726 return f.read()
729 727 except IOError as msg:
730 728 if Path(msg.filename) == filepath:
731 729 warn('File not found. Did you forget to save?')
732 730 return
733 731 else:
734 732 self.shell.showtraceback()
General Comments 0
You need to be logged in to leave comments. Login now