##// END OF EJS Templates
make hlen_before_cell a function argument instead of class member
Julian Taylor -
Show More
@@ -1,616 +1,625 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Subclass of InteractiveShell for terminal based frontends."""
3 3
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de>
6 6 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
7 7 # Copyright (C) 2008-2010 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 import __builtin__
18 18 import bdb
19 19 import os
20 20 import re
21 21 import sys
22 22
23 23 try:
24 24 from contextlib import nested
25 25 except:
26 26 from IPython.utils.nested_context import nested
27 27
28 28 from IPython.core.error import TryNext
29 29 from IPython.core.usage import interactive_usage, default_banner
30 30 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
31 31 from IPython.lib.inputhook import enable_gui
32 32 from IPython.lib.pylabtools import pylab_activate
33 33 from IPython.testing.skipdoctest import skip_doctest
34 34 from IPython.utils import py3compat
35 35 from IPython.utils.terminal import toggle_set_term_title, set_term_title
36 36 from IPython.utils.process import abbrev_cwd
37 37 from IPython.utils.warn import warn
38 38 from IPython.utils.text import num_ini_spaces
39 39 from IPython.utils.traitlets import Int, CBool, Unicode
40 40
41 41 #-----------------------------------------------------------------------------
42 42 # Utilities
43 43 #-----------------------------------------------------------------------------
44 44
45 45 def get_default_editor():
46 46 try:
47 47 ed = os.environ['EDITOR']
48 48 except KeyError:
49 49 if os.name == 'posix':
50 50 ed = 'vi' # the only one guaranteed to be there!
51 51 else:
52 52 ed = 'notepad' # same in Windows!
53 53 return ed
54 54
55 55 #-----------------------------------------------------------------------------
56 56 # Main class
57 57 #-----------------------------------------------------------------------------
58 58
59 59 class TerminalInteractiveShell(InteractiveShell):
60 60
61 61 autoedit_syntax = CBool(False, config=True,
62 62 help="auto editing of files with syntax errors.")
63 63 banner = Unicode('')
64 64 banner1 = Unicode(default_banner, config=True,
65 65 help="""The part of the banner to be printed before the profile"""
66 66 )
67 67 banner2 = Unicode('', config=True,
68 68 help="""The part of the banner to be printed after the profile"""
69 69 )
70 70 confirm_exit = CBool(True, config=True,
71 71 help="""
72 72 Set to confirm when you try to exit IPython with an EOF (Control-D
73 73 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
74 74 you can force a direct exit without any confirmation.""",
75 75 )
76 76 # This display_banner only controls whether or not self.show_banner()
77 77 # is called when mainloop/interact are called. The default is False
78 78 # because for the terminal based application, the banner behavior
79 79 # is controlled by Global.display_banner, which IPythonApp looks at
80 80 # to determine if *it* should call show_banner() by hand or not.
81 81 display_banner = CBool(False) # This isn't configurable!
82 82 embedded = CBool(False)
83 83 embedded_active = CBool(False)
84 84 editor = Unicode(get_default_editor(), config=True,
85 85 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
86 86 )
87 87 pager = Unicode('less', config=True,
88 88 help="The shell program to be used for paging.")
89 89
90 90 screen_length = Int(0, config=True,
91 91 help=
92 92 """Number of lines of your screen, used to control printing of very
93 93 long strings. Strings longer than this number of lines will be sent
94 94 through a pager instead of directly printed. The default value for
95 95 this is 0, which means IPython will auto-detect your screen size every
96 96 time it needs to print certain potentially long strings (this doesn't
97 97 change the behavior of the 'print' keyword, it's only triggered
98 98 internally). If for some reason this isn't working well (it needs
99 99 curses support), specify it yourself. Otherwise don't change the
100 100 default.""",
101 101 )
102 102 term_title = CBool(False, config=True,
103 103 help="Enable auto setting the terminal title."
104 104 )
105 105
106 106 def __init__(self, config=None, ipython_dir=None, profile_dir=None, user_ns=None,
107 107 user_global_ns=None, custom_exceptions=((),None),
108 108 usage=None, banner1=None, banner2=None,
109 109 display_banner=None):
110 110
111 111 super(TerminalInteractiveShell, self).__init__(
112 112 config=config, profile_dir=profile_dir, user_ns=user_ns,
113 113 user_global_ns=user_global_ns, custom_exceptions=custom_exceptions
114 114 )
115 115 # use os.system instead of utils.process.system by default, except on Windows
116 116 if os.name == 'nt':
117 117 self.system = self.system_piped
118 118 else:
119 119 self.system = self.system_raw
120 120
121 121 self.init_term_title()
122 122 self.init_usage(usage)
123 123 self.init_banner(banner1, banner2, display_banner)
124 124
125 125 #-------------------------------------------------------------------------
126 126 # Things related to the terminal
127 127 #-------------------------------------------------------------------------
128 128
129 129 @property
130 130 def usable_screen_length(self):
131 131 if self.screen_length == 0:
132 132 return 0
133 133 else:
134 134 num_lines_bot = self.separate_in.count('\n')+1
135 135 return self.screen_length - num_lines_bot
136 136
137 137 def init_term_title(self):
138 138 # Enable or disable the terminal title.
139 139 if self.term_title:
140 140 toggle_set_term_title(True)
141 141 set_term_title('IPython: ' + abbrev_cwd())
142 142 else:
143 143 toggle_set_term_title(False)
144 144
145 145 #-------------------------------------------------------------------------
146 146 # Things related to aliases
147 147 #-------------------------------------------------------------------------
148 148
149 149 def init_alias(self):
150 150 # The parent class defines aliases that can be safely used with any
151 151 # frontend.
152 152 super(TerminalInteractiveShell, self).init_alias()
153 153
154 154 # Now define aliases that only make sense on the terminal, because they
155 155 # need direct access to the console in a way that we can't emulate in
156 156 # GUI or web frontend
157 157 if os.name == 'posix':
158 158 aliases = [('clear', 'clear'), ('more', 'more'), ('less', 'less'),
159 159 ('man', 'man')]
160 160 elif os.name == 'nt':
161 161 aliases = [('cls', 'cls')]
162 162
163 163
164 164 for name, cmd in aliases:
165 165 self.alias_manager.define_alias(name, cmd)
166 166
167 167 #-------------------------------------------------------------------------
168 168 # Things related to the banner and usage
169 169 #-------------------------------------------------------------------------
170 170
171 171 def _banner1_changed(self):
172 172 self.compute_banner()
173 173
174 174 def _banner2_changed(self):
175 175 self.compute_banner()
176 176
177 177 def _term_title_changed(self, name, new_value):
178 178 self.init_term_title()
179 179
180 180 def init_banner(self, banner1, banner2, display_banner):
181 181 if banner1 is not None:
182 182 self.banner1 = banner1
183 183 if banner2 is not None:
184 184 self.banner2 = banner2
185 185 if display_banner is not None:
186 186 self.display_banner = display_banner
187 187 self.compute_banner()
188 188
189 189 def show_banner(self, banner=None):
190 190 if banner is None:
191 191 banner = self.banner
192 192 self.write(banner)
193 193
194 194 def compute_banner(self):
195 195 self.banner = self.banner1
196 196 if self.profile and self.profile != 'default':
197 197 self.banner += '\nIPython profile: %s\n' % self.profile
198 198 if self.banner2:
199 199 self.banner += '\n' + self.banner2
200 200
201 201 def init_usage(self, usage=None):
202 202 if usage is None:
203 203 self.usage = interactive_usage
204 204 else:
205 205 self.usage = usage
206 206
207 207 #-------------------------------------------------------------------------
208 208 # Mainloop and code execution logic
209 209 #-------------------------------------------------------------------------
210 210
211 211 def mainloop(self, display_banner=None):
212 212 """Start the mainloop.
213 213
214 214 If an optional banner argument is given, it will override the
215 215 internally created default banner.
216 216 """
217 217
218 218 with nested(self.builtin_trap, self.display_trap):
219 219
220 220 while 1:
221 221 try:
222 222 self.interact(display_banner=display_banner)
223 223 #self.interact_with_readline()
224 224 # XXX for testing of a readline-decoupled repl loop, call
225 225 # interact_with_readline above
226 226 break
227 227 except KeyboardInterrupt:
228 228 # this should not be necessary, but KeyboardInterrupt
229 229 # handling seems rather unpredictable...
230 230 self.write("\nKeyboardInterrupt in interact()\n")
231 231
232 def _replace_rlhist_multiline(self, source_raw):
232 def _replace_rlhist_multiline(self, source_raw, hlen_before_cell):
233 233 """Store multiple lines as a single entry in history"""
234 if self.multiline_history and self.has_readline and source_raw.rstrip():
235 hlen = self.readline.get_current_history_length()
236 234
237 # nothing changed do nothing, e.g. when rl removes consecutive dups
238 if self.hlen_before_cell == hlen:
239 return
235 # do nothing without readline or disabled multiline
236 if not self.has_readline or not self.multiline_history:
237 return hlen_before_cell
238
239 # skip empty cells
240 if not source_raw.rstrip():
241 return hlen_before_cell
242
243 # nothing changed do nothing, e.g. when rl removes consecutive dups
244 hlen = self.readline.get_current_history_length()
245 if hlen == hlen_before_cell:
246 return hlen_before_cell
240 247
241 for i in range(hlen - self.hlen_before_cell):
242 self.readline.remove_history_item(hlen - i - 1)
243 stdin_encoding = sys.stdin.encoding or "utf-8"
244 self.readline.add_history(py3compat.unicode_to_str(source_raw.rstrip(),
245 stdin_encoding))
246 self.hlen_before_cell = self.readline.get_current_history_length()
248 for i in range(hlen - hlen_before_cell):
249 self.readline.remove_history_item(hlen - i - 1)
250 stdin_encoding = sys.stdin.encoding or "utf-8"
251 self.readline.add_history(py3compat.unicode_to_str(source_raw.rstrip(),
252 stdin_encoding))
253 return self.readline.get_current_history_length()
247 254
248 255 def interact(self, display_banner=None):
249 256 """Closely emulate the interactive Python console."""
250 257
251 258 # batch run -> do not interact
252 259 if self.exit_now:
253 260 return
254 261
255 262 if display_banner is None:
256 263 display_banner = self.display_banner
257 264
258 265 if isinstance(display_banner, basestring):
259 266 self.show_banner(display_banner)
260 267 elif display_banner:
261 268 self.show_banner()
262 269
263 270 more = False
264 271
265 272 # Mark activity in the builtins
266 273 __builtin__.__dict__['__IPYTHON__active'] += 1
267 274
268 275 if self.has_readline:
269 276 self.readline_startup_hook(self.pre_readline)
270 self.hlen_before_cell = self.readline.get_current_history_length()
277 hlen_b4_cell = self.readline.get_current_history_length()
271 278 # exit_now is set by a call to %Exit or %Quit, through the
272 279 # ask_exit callback.
273 280
274 281 while not self.exit_now:
275 282 self.hooks.pre_prompt_hook()
276 283 if more:
277 284 try:
278 285 prompt = self.hooks.generate_prompt(True)
279 286 except:
280 287 self.showtraceback()
281 288 if self.autoindent:
282 289 self.rl_do_indent = True
283 290
284 291 else:
285 292 try:
286 293 prompt = self.hooks.generate_prompt(False)
287 294 except:
288 295 self.showtraceback()
289 296 try:
290 297 line = self.raw_input(prompt)
291 298 if self.exit_now:
292 299 # quick exit on sys.std[in|out] close
293 300 break
294 301 if self.autoindent:
295 302 self.rl_do_indent = False
296 303
297 304 except KeyboardInterrupt:
298 305 #double-guard against keyboardinterrupts during kbdint handling
299 306 try:
300 307 self.write('\nKeyboardInterrupt\n')
301 308 source_raw = self.input_splitter.source_raw_reset()[1]
302 self._replace_rlhist_multiline(source_raw)
309 hlen_b4_cell = \
310 self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
303 311 more = False
304 312 except KeyboardInterrupt:
305 313 pass
306 314 except EOFError:
307 315 if self.autoindent:
308 316 self.rl_do_indent = False
309 317 if self.has_readline:
310 318 self.readline_startup_hook(None)
311 319 self.write('\n')
312 320 self.exit()
313 321 except bdb.BdbQuit:
314 322 warn('The Python debugger has exited with a BdbQuit exception.\n'
315 323 'Because of how pdb handles the stack, it is impossible\n'
316 324 'for IPython to properly format this particular exception.\n'
317 325 'IPython will resume normal operation.')
318 326 except:
319 327 # exceptions here are VERY RARE, but they can be triggered
320 328 # asynchronously by signal handlers, for example.
321 329 self.showtraceback()
322 330 else:
323 331 self.input_splitter.push(line)
324 332 more = self.input_splitter.push_accepts_more()
325 333 if (self.SyntaxTB.last_syntax_error and
326 334 self.autoedit_syntax):
327 335 self.edit_syntax_error()
328 336 if not more:
329 337 source_raw = self.input_splitter.source_raw_reset()[1]
330 338 self.run_cell(source_raw, store_history=True)
331 self._replace_rlhist_multiline(source_raw)
339 hlen_b4_cell = \
340 self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
332 341
333 342 # We are off again...
334 343 __builtin__.__dict__['__IPYTHON__active'] -= 1
335 344
336 345 # Turn off the exit flag, so the mainloop can be restarted if desired
337 346 self.exit_now = False
338 347
339 348 def raw_input(self, prompt=''):
340 349 """Write a prompt and read a line.
341 350
342 351 The returned line does not include the trailing newline.
343 352 When the user enters the EOF key sequence, EOFError is raised.
344 353
345 354 Optional inputs:
346 355
347 356 - prompt(''): a string to be printed to prompt the user.
348 357
349 358 - continue_prompt(False): whether this line is the first one or a
350 359 continuation in a sequence of inputs.
351 360 """
352 361 # Code run by the user may have modified the readline completer state.
353 362 # We must ensure that our completer is back in place.
354 363
355 364 if self.has_readline:
356 365 self.set_readline_completer()
357 366
358 367 try:
359 368 line = py3compat.str_to_unicode(self.raw_input_original(prompt))
360 369 except ValueError:
361 370 warn("\n********\nYou or a %run:ed script called sys.stdin.close()"
362 371 " or sys.stdout.close()!\nExiting IPython!")
363 372 self.ask_exit()
364 373 return ""
365 374
366 375 # Try to be reasonably smart about not re-indenting pasted input more
367 376 # than necessary. We do this by trimming out the auto-indent initial
368 377 # spaces, if the user's actual input started itself with whitespace.
369 378 if self.autoindent:
370 379 if num_ini_spaces(line) > self.indent_current_nsp:
371 380 line = line[self.indent_current_nsp:]
372 381 self.indent_current_nsp = 0
373 382
374 383 return line
375 384
376 385 #-------------------------------------------------------------------------
377 386 # Methods to support auto-editing of SyntaxErrors.
378 387 #-------------------------------------------------------------------------
379 388
380 389 def edit_syntax_error(self):
381 390 """The bottom half of the syntax error handler called in the main loop.
382 391
383 392 Loop until syntax error is fixed or user cancels.
384 393 """
385 394
386 395 while self.SyntaxTB.last_syntax_error:
387 396 # copy and clear last_syntax_error
388 397 err = self.SyntaxTB.clear_err_state()
389 398 if not self._should_recompile(err):
390 399 return
391 400 try:
392 401 # may set last_syntax_error again if a SyntaxError is raised
393 402 self.safe_execfile(err.filename,self.user_ns)
394 403 except:
395 404 self.showtraceback()
396 405 else:
397 406 try:
398 407 f = file(err.filename)
399 408 try:
400 409 # This should be inside a display_trap block and I
401 410 # think it is.
402 411 sys.displayhook(f.read())
403 412 finally:
404 413 f.close()
405 414 except:
406 415 self.showtraceback()
407 416
408 417 def _should_recompile(self,e):
409 418 """Utility routine for edit_syntax_error"""
410 419
411 420 if e.filename in ('<ipython console>','<input>','<string>',
412 421 '<console>','<BackgroundJob compilation>',
413 422 None):
414 423
415 424 return False
416 425 try:
417 426 if (self.autoedit_syntax and
418 427 not self.ask_yes_no('Return to editor to correct syntax error? '
419 428 '[Y/n] ','y')):
420 429 return False
421 430 except EOFError:
422 431 return False
423 432
424 433 def int0(x):
425 434 try:
426 435 return int(x)
427 436 except TypeError:
428 437 return 0
429 438 # always pass integer line and offset values to editor hook
430 439 try:
431 440 self.hooks.fix_error_editor(e.filename,
432 441 int0(e.lineno),int0(e.offset),e.msg)
433 442 except TryNext:
434 443 warn('Could not open editor')
435 444 return False
436 445 return True
437 446
438 447 #-------------------------------------------------------------------------
439 448 # Things related to GUI support and pylab
440 449 #-------------------------------------------------------------------------
441 450
442 451 def enable_pylab(self, gui=None, import_all=True):
443 452 """Activate pylab support at runtime.
444 453
445 454 This turns on support for matplotlib, preloads into the interactive
446 455 namespace all of numpy and pylab, and configures IPython to correcdtly
447 456 interact with the GUI event loop. The GUI backend to be used can be
448 457 optionally selected with the optional :param:`gui` argument.
449 458
450 459 Parameters
451 460 ----------
452 461 gui : optional, string
453 462
454 463 If given, dictates the choice of matplotlib GUI backend to use
455 464 (should be one of IPython's supported backends, 'tk', 'qt', 'wx' or
456 465 'gtk'), otherwise we use the default chosen by matplotlib (as
457 466 dictated by the matplotlib build-time options plus the user's
458 467 matplotlibrc configuration file).
459 468 """
460 469 # We want to prevent the loading of pylab to pollute the user's
461 470 # namespace as shown by the %who* magics, so we execute the activation
462 471 # code in an empty namespace, and we update *both* user_ns and
463 472 # user_ns_hidden with this information.
464 473 ns = {}
465 474 gui = pylab_activate(ns, gui, import_all)
466 475 self.user_ns.update(ns)
467 476 self.user_ns_hidden.update(ns)
468 477 # Now we must activate the gui pylab wants to use, and fix %run to take
469 478 # plot updates into account
470 479 enable_gui(gui)
471 480 self.magic_run = self._pylab_magic_run
472 481
473 482 #-------------------------------------------------------------------------
474 483 # Things related to exiting
475 484 #-------------------------------------------------------------------------
476 485
477 486 def ask_exit(self):
478 487 """ Ask the shell to exit. Can be overiden and used as a callback. """
479 488 self.exit_now = True
480 489
481 490 def exit(self):
482 491 """Handle interactive exit.
483 492
484 493 This method calls the ask_exit callback."""
485 494 if self.confirm_exit:
486 495 if self.ask_yes_no('Do you really want to exit ([y]/n)?','y'):
487 496 self.ask_exit()
488 497 else:
489 498 self.ask_exit()
490 499
491 500 #------------------------------------------------------------------------
492 501 # Magic overrides
493 502 #------------------------------------------------------------------------
494 503 # Once the base class stops inheriting from magic, this code needs to be
495 504 # moved into a separate machinery as well. For now, at least isolate here
496 505 # the magics which this class needs to implement differently from the base
497 506 # class, or that are unique to it.
498 507
499 508 def magic_autoindent(self, parameter_s = ''):
500 509 """Toggle autoindent on/off (if available)."""
501 510
502 511 self.shell.set_autoindent()
503 512 print "Automatic indentation is:",['OFF','ON'][self.shell.autoindent]
504 513
505 514 @skip_doctest
506 515 def magic_cpaste(self, parameter_s=''):
507 516 """Paste & execute a pre-formatted code block from clipboard.
508 517
509 518 You must terminate the block with '--' (two minus-signs) or Ctrl-D alone on the
510 519 line. You can also provide your own sentinel with '%paste -s %%' ('%%'
511 520 is the new sentinel for this operation)
512 521
513 522 The block is dedented prior to execution to enable execution of method
514 523 definitions. '>' and '+' characters at the beginning of a line are
515 524 ignored, to allow pasting directly from e-mails, diff files and
516 525 doctests (the '...' continuation prompt is also stripped). The
517 526 executed block is also assigned to variable named 'pasted_block' for
518 527 later editing with '%edit pasted_block'.
519 528
520 529 You can also pass a variable name as an argument, e.g. '%cpaste foo'.
521 530 This assigns the pasted block to variable 'foo' as string, without
522 531 dedenting or executing it (preceding >>> and + is still stripped)
523 532
524 533 '%cpaste -r' re-executes the block previously entered by cpaste.
525 534
526 535 Do not be alarmed by garbled output on Windows (it's a readline bug).
527 536 Just press enter and type -- (and press enter again) and the block
528 537 will be what was just pasted.
529 538
530 539 IPython statements (magics, shell escapes) are not supported (yet).
531 540
532 541 See also
533 542 --------
534 543 paste: automatically pull code from clipboard.
535 544
536 545 Examples
537 546 --------
538 547 ::
539 548
540 549 In [8]: %cpaste
541 550 Pasting code; enter '--' alone on the line to stop.
542 551 :>>> a = ["world!", "Hello"]
543 552 :>>> print " ".join(sorted(a))
544 553 :--
545 554 Hello world!
546 555 """
547 556
548 557 opts,args = self.parse_options(parameter_s,'rs:',mode='string')
549 558 par = args.strip()
550 559 if opts.has_key('r'):
551 560 self._rerun_pasted()
552 561 return
553 562
554 563 sentinel = opts.get('s','--')
555 564
556 565 block = self._strip_pasted_lines_for_code(
557 566 self._get_pasted_lines(sentinel))
558 567
559 568 self._execute_block(block, par)
560 569
561 570 def magic_paste(self, parameter_s=''):
562 571 """Paste & execute a pre-formatted code block from clipboard.
563 572
564 573 The text is pulled directly from the clipboard without user
565 574 intervention and printed back on the screen before execution (unless
566 575 the -q flag is given to force quiet mode).
567 576
568 577 The block is dedented prior to execution to enable execution of method
569 578 definitions. '>' and '+' characters at the beginning of a line are
570 579 ignored, to allow pasting directly from e-mails, diff files and
571 580 doctests (the '...' continuation prompt is also stripped). The
572 581 executed block is also assigned to variable named 'pasted_block' for
573 582 later editing with '%edit pasted_block'.
574 583
575 584 You can also pass a variable name as an argument, e.g. '%paste foo'.
576 585 This assigns the pasted block to variable 'foo' as string, without
577 586 dedenting or executing it (preceding >>> and + is still stripped)
578 587
579 588 Options
580 589 -------
581 590
582 591 -r: re-executes the block previously entered by cpaste.
583 592
584 593 -q: quiet mode: do not echo the pasted text back to the terminal.
585 594
586 595 IPython statements (magics, shell escapes) are not supported (yet).
587 596
588 597 See also
589 598 --------
590 599 cpaste: manually paste code into terminal until you mark its end.
591 600 """
592 601 opts,args = self.parse_options(parameter_s,'rq',mode='string')
593 602 par = args.strip()
594 603 if opts.has_key('r'):
595 604 self._rerun_pasted()
596 605 return
597 606
598 607 text = self.shell.hooks.clipboard_get()
599 608 block = self._strip_pasted_lines_for_code(text.splitlines())
600 609
601 610 # By default, echo back to terminal unless quiet mode is requested
602 611 if not opts.has_key('q'):
603 612 write = self.shell.write
604 613 write(self.shell.pycolorize(block))
605 614 if not block.endswith('\n'):
606 615 write('\n')
607 616 write("## -- End pasted text --\n")
608 617
609 618 self._execute_block(block, par)
610 619
611 620 def showindentationerror(self):
612 621 super(TerminalInteractiveShell, self).showindentationerror()
613 622 print("If you want to paste code into IPython, try the %paste magic function.")
614 623
615 624
616 625 InteractiveShellABC.register(TerminalInteractiveShell)
@@ -1,137 +1,142 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for the key interactiveshell module.
3 3
4 4 Authors
5 5 -------
6 6 * Julian Taylor
7 7 """
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2011 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18 # stdlib
19 19 import unittest
20 20
21 21 from IPython.testing.decorators import skipif
22 22
23 23 class InteractiveShellTestCase(unittest.TestCase):
24 24 def rl_hist_entries(self, rl, n):
25 25 """Get last n readline history entries as a list"""
26 26 return [rl.get_history_item(rl.get_current_history_length() - x)
27 27 for x in range(n - 1, -1, -1)]
28 28
29 29 def test_runs_without_rl(self):
30 30 """Test that function does not throw without readline"""
31 31 ip = get_ipython()
32 32 ip.has_readline = False
33 33 ip.readline = None
34 ip._replace_rlhist_multiline(u'source')
34 ip._replace_rlhist_multiline(u'source', 0)
35 35
36 36 @skipif(not get_ipython().has_readline, 'no readline')
37 37 def test_replace_multiline_hist_disabled(self):
38 38 """Test that multiline replace does nothing if disabled"""
39 39 ip = get_ipython()
40 40 ip.multiline_history = False
41 41
42 42 ghist = [u'line1', u'line2']
43 43 for h in ghist:
44 44 ip.readline.add_history(h)
45 ip.hlen_before_cell = ip.readline.get_current_history_length()
46 ip._replace_rlhist_multiline(u'sourc€\nsource2')
45 hlen_b4_cell = ip.readline.get_current_history_length()
46 hlen_b4_cell = ip._replace_rlhist_multiline(u'sourc€\nsource2',
47 hlen_b4_cell)
47 48
48 49 self.assertEquals(ip.readline.get_current_history_length(),
49 ip.hlen_before_cell)
50 hlen_b4_cell)
50 51 hist = self.rl_hist_entries(ip.readline, 2)
51 52 self.assertEquals(hist, ghist)
52 53
53 54 @skipif(not get_ipython().has_readline, 'no readline')
54 55 def test_replace_multiline_hist_adds(self):
55 56 """Test that multiline replace function adds history"""
56 57 ip = get_ipython()
57 58
58 ip.hlen_before_cell = ip.readline.get_current_history_length()
59 ip._replace_rlhist_multiline(u'sourc€')
59 hlen_b4_cell = ip.readline.get_current_history_length()
60 hlen_b4_cell = ip._replace_rlhist_multiline(u'sourc€', hlen_b4_cell)
60 61
61 self.assertEquals(ip.hlen_before_cell,
62 self.assertEquals(hlen_b4_cell,
62 63 ip.readline.get_current_history_length())
63 64
64 65 @skipif(not get_ipython().has_readline, 'no readline')
65 66 def test_replace_multiline_hist_keeps_history(self):
66 67 """Test that multiline replace does not delete history"""
67 68 ip = get_ipython()
68 69 ip.multiline_history = True
69 70
70 71 ghist = [u'line1', u'line2']
71 72 for h in ghist:
72 73 ip.readline.add_history(h)
73 74
74 75 #start cell
75 ip.hlen_before_cell = ip.readline.get_current_history_length()
76 # nothing added to rl history, should do nothing
77 ip._replace_rlhist_multiline(u'sourc€\nsource2')
76 hlen_b4_cell = ip.readline.get_current_history_length()
77 # nothing added to rl history, should do nothing
78 hlen_b4_cell = ip._replace_rlhist_multiline(u'sourc€\nsource2',
79 hlen_b4_cell)
78 80
79 81 self.assertEquals(ip.readline.get_current_history_length(),
80 ip.hlen_before_cell)
82 hlen_b4_cell)
81 83 hist = self.rl_hist_entries(ip.readline, 2)
82 84 self.assertEquals(hist, ghist)
83 85
84 86
85 87 @skipif(not get_ipython().has_readline, 'no readline')
86 88 def test_replace_multiline_hist_replaces_twice(self):
87 89 """Test that multiline entries are replaced twice"""
88 90 ip = get_ipython()
89 91 ip.multiline_history = True
90 92
91 93 ip.readline.add_history(u'line0')
92 94 #start cell
93 ip.hlen_before_cell = ip.readline.get_current_history_length()
95 hlen_b4_cell = ip.readline.get_current_history_length()
94 96 ip.readline.add_history('l€ne1')
95 97 ip.readline.add_history('line2')
96 98 #replace cell with single line
97 ip._replace_rlhist_multiline(u'l€ne1\nline2')
99 hlen_b4_cell = ip._replace_rlhist_multiline(u'l€ne1\nline2',
100 hlen_b4_cell)
98 101 ip.readline.add_history('l€ne3')
99 102 ip.readline.add_history('line4')
100 103 #replace cell with single line
101 ip._replace_rlhist_multiline(u'l€ne3\nline4')
104 hlen_b4_cell = ip._replace_rlhist_multiline(u'l€ne3\nline4',
105 hlen_b4_cell)
102 106
103 107 self.assertEquals(ip.readline.get_current_history_length(),
104 ip.hlen_before_cell)
108 hlen_b4_cell)
105 109 hist = self.rl_hist_entries(ip.readline, 3)
106 110 self.assertEquals(hist, ['line0', 'l€ne1\nline2', 'l€ne3\nline4'])
107 111
108 112
109 113 @skipif(not get_ipython().has_readline, 'no readline')
110 114 def test_replace_multiline_hist_replaces_empty_line(self):
111 115 """Test that multiline history skips empty line cells"""
112 116 ip = get_ipython()
113 117 ip.multiline_history = True
114 118
115 119 ip.readline.add_history(u'line0')
116 120 #start cell
117 ip.hlen_before_cell = ip.readline.get_current_history_length()
121 hlen_b4_cell = ip.readline.get_current_history_length()
118 122 ip.readline.add_history('l€ne1')
119 123 ip.readline.add_history('line2')
120 ip._replace_rlhist_multiline(u'l€ne1\nline2')
124 hlen_b4_cell = ip._replace_rlhist_multiline(u'l€ne1\nline2',
125 hlen_b4_cell)
121 126 ip.readline.add_history('')
122 ip._replace_rlhist_multiline(u'')
127 hlen_b4_cell = ip._replace_rlhist_multiline(u'', hlen_b4_cell)
123 128 ip.readline.add_history('l€ne3')
124 ip._replace_rlhist_multiline(u'l€ne3')
129 hlen_b4_cell = ip._replace_rlhist_multiline(u'l€ne3', hlen_b4_cell)
125 130 ip.readline.add_history(' ')
126 ip._replace_rlhist_multiline(' ')
131 hlen_b4_cell = ip._replace_rlhist_multiline(' ', hlen_b4_cell)
127 132 ip.readline.add_history('\t')
128 133 ip.readline.add_history('\t ')
129 ip._replace_rlhist_multiline('\t')
134 hlen_b4_cell = ip._replace_rlhist_multiline('\t', hlen_b4_cell)
130 135 ip.readline.add_history('line4')
131 ip._replace_rlhist_multiline(u'line4')
136 hlen_b4_cell = ip._replace_rlhist_multiline(u'line4', hlen_b4_cell)
132 137
133 138 self.assertEquals(ip.readline.get_current_history_length(),
134 ip.hlen_before_cell)
139 hlen_b4_cell)
135 140 hist = self.rl_hist_entries(ip.readline, 4)
136 141 # expect no empty cells in history
137 142 self.assertEquals(hist, ['line0', 'l€ne1\nline2', 'l€ne3', 'line4'])
General Comments 0
You need to be logged in to leave comments. Login now