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