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