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