##// END OF EJS Templates
don't try to replace history when rl history unchanged...
Julian Taylor -
Show More
@@ -1,611 +1,616 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 232 def _replace_rlhist_multiline(self, source_raw):
233 233 """Store multiple lines as a single entry in history"""
234 234 if self.multiline_history and self.has_readline and source_raw.rstrip():
235 235 hlen = self.readline.get_current_history_length()
236
237 # nothing changed do nothing, e.g. when rl removes consecutive dups
238 if self.hlen_before_cell == hlen:
239 return
240
236 241 for i in range(hlen - self.hlen_before_cell):
237 242 self.readline.remove_history_item(hlen - i - 1)
238 243 stdin_encoding = sys.stdin.encoding or "utf-8"
239 244 self.readline.add_history(py3compat.unicode_to_str(source_raw.rstrip(),
240 245 stdin_encoding))
241 246 self.hlen_before_cell = self.readline.get_current_history_length()
242 247
243 248 def interact(self, display_banner=None):
244 249 """Closely emulate the interactive Python console."""
245 250
246 251 # batch run -> do not interact
247 252 if self.exit_now:
248 253 return
249 254
250 255 if display_banner is None:
251 256 display_banner = self.display_banner
252 257
253 258 if isinstance(display_banner, basestring):
254 259 self.show_banner(display_banner)
255 260 elif display_banner:
256 261 self.show_banner()
257 262
258 263 more = False
259 264
260 265 # Mark activity in the builtins
261 266 __builtin__.__dict__['__IPYTHON__active'] += 1
262 267
263 268 if self.has_readline:
264 269 self.readline_startup_hook(self.pre_readline)
265 270 self.hlen_before_cell = self.readline.get_current_history_length()
266 271 # exit_now is set by a call to %Exit or %Quit, through the
267 272 # ask_exit callback.
268 273
269 274 while not self.exit_now:
270 275 self.hooks.pre_prompt_hook()
271 276 if more:
272 277 try:
273 278 prompt = self.hooks.generate_prompt(True)
274 279 except:
275 280 self.showtraceback()
276 281 if self.autoindent:
277 282 self.rl_do_indent = True
278 283
279 284 else:
280 285 try:
281 286 prompt = self.hooks.generate_prompt(False)
282 287 except:
283 288 self.showtraceback()
284 289 try:
285 290 line = self.raw_input(prompt)
286 291 if self.exit_now:
287 292 # quick exit on sys.std[in|out] close
288 293 break
289 294 if self.autoindent:
290 295 self.rl_do_indent = False
291 296
292 297 except KeyboardInterrupt:
293 298 #double-guard against keyboardinterrupts during kbdint handling
294 299 try:
295 300 self.write('\nKeyboardInterrupt\n')
296 301 source_raw = self.input_splitter.source_raw_reset()[1]
297 302 self._replace_rlhist_multiline(source_raw)
298 303 more = False
299 304 except KeyboardInterrupt:
300 305 pass
301 306 except EOFError:
302 307 if self.autoindent:
303 308 self.rl_do_indent = False
304 309 if self.has_readline:
305 310 self.readline_startup_hook(None)
306 311 self.write('\n')
307 312 self.exit()
308 313 except bdb.BdbQuit:
309 314 warn('The Python debugger has exited with a BdbQuit exception.\n'
310 315 'Because of how pdb handles the stack, it is impossible\n'
311 316 'for IPython to properly format this particular exception.\n'
312 317 'IPython will resume normal operation.')
313 318 except:
314 319 # exceptions here are VERY RARE, but they can be triggered
315 320 # asynchronously by signal handlers, for example.
316 321 self.showtraceback()
317 322 else:
318 323 self.input_splitter.push(line)
319 324 more = self.input_splitter.push_accepts_more()
320 325 if (self.SyntaxTB.last_syntax_error and
321 326 self.autoedit_syntax):
322 327 self.edit_syntax_error()
323 328 if not more:
324 329 source_raw = self.input_splitter.source_raw_reset()[1]
325 330 self.run_cell(source_raw, store_history=True)
326 331 self._replace_rlhist_multiline(source_raw)
327 332
328 333 # We are off again...
329 334 __builtin__.__dict__['__IPYTHON__active'] -= 1
330 335
331 336 # Turn off the exit flag, so the mainloop can be restarted if desired
332 337 self.exit_now = False
333 338
334 339 def raw_input(self, prompt=''):
335 340 """Write a prompt and read a line.
336 341
337 342 The returned line does not include the trailing newline.
338 343 When the user enters the EOF key sequence, EOFError is raised.
339 344
340 345 Optional inputs:
341 346
342 347 - prompt(''): a string to be printed to prompt the user.
343 348
344 349 - continue_prompt(False): whether this line is the first one or a
345 350 continuation in a sequence of inputs.
346 351 """
347 352 # Code run by the user may have modified the readline completer state.
348 353 # We must ensure that our completer is back in place.
349 354
350 355 if self.has_readline:
351 356 self.set_readline_completer()
352 357
353 358 try:
354 359 line = py3compat.str_to_unicode(self.raw_input_original(prompt))
355 360 except ValueError:
356 361 warn("\n********\nYou or a %run:ed script called sys.stdin.close()"
357 362 " or sys.stdout.close()!\nExiting IPython!")
358 363 self.ask_exit()
359 364 return ""
360 365
361 366 # Try to be reasonably smart about not re-indenting pasted input more
362 367 # than necessary. We do this by trimming out the auto-indent initial
363 368 # spaces, if the user's actual input started itself with whitespace.
364 369 if self.autoindent:
365 370 if num_ini_spaces(line) > self.indent_current_nsp:
366 371 line = line[self.indent_current_nsp:]
367 372 self.indent_current_nsp = 0
368 373
369 374 return line
370 375
371 376 #-------------------------------------------------------------------------
372 377 # Methods to support auto-editing of SyntaxErrors.
373 378 #-------------------------------------------------------------------------
374 379
375 380 def edit_syntax_error(self):
376 381 """The bottom half of the syntax error handler called in the main loop.
377 382
378 383 Loop until syntax error is fixed or user cancels.
379 384 """
380 385
381 386 while self.SyntaxTB.last_syntax_error:
382 387 # copy and clear last_syntax_error
383 388 err = self.SyntaxTB.clear_err_state()
384 389 if not self._should_recompile(err):
385 390 return
386 391 try:
387 392 # may set last_syntax_error again if a SyntaxError is raised
388 393 self.safe_execfile(err.filename,self.user_ns)
389 394 except:
390 395 self.showtraceback()
391 396 else:
392 397 try:
393 398 f = file(err.filename)
394 399 try:
395 400 # This should be inside a display_trap block and I
396 401 # think it is.
397 402 sys.displayhook(f.read())
398 403 finally:
399 404 f.close()
400 405 except:
401 406 self.showtraceback()
402 407
403 408 def _should_recompile(self,e):
404 409 """Utility routine for edit_syntax_error"""
405 410
406 411 if e.filename in ('<ipython console>','<input>','<string>',
407 412 '<console>','<BackgroundJob compilation>',
408 413 None):
409 414
410 415 return False
411 416 try:
412 417 if (self.autoedit_syntax and
413 418 not self.ask_yes_no('Return to editor to correct syntax error? '
414 419 '[Y/n] ','y')):
415 420 return False
416 421 except EOFError:
417 422 return False
418 423
419 424 def int0(x):
420 425 try:
421 426 return int(x)
422 427 except TypeError:
423 428 return 0
424 429 # always pass integer line and offset values to editor hook
425 430 try:
426 431 self.hooks.fix_error_editor(e.filename,
427 432 int0(e.lineno),int0(e.offset),e.msg)
428 433 except TryNext:
429 434 warn('Could not open editor')
430 435 return False
431 436 return True
432 437
433 438 #-------------------------------------------------------------------------
434 439 # Things related to GUI support and pylab
435 440 #-------------------------------------------------------------------------
436 441
437 442 def enable_pylab(self, gui=None, import_all=True):
438 443 """Activate pylab support at runtime.
439 444
440 445 This turns on support for matplotlib, preloads into the interactive
441 446 namespace all of numpy and pylab, and configures IPython to correcdtly
442 447 interact with the GUI event loop. The GUI backend to be used can be
443 448 optionally selected with the optional :param:`gui` argument.
444 449
445 450 Parameters
446 451 ----------
447 452 gui : optional, string
448 453
449 454 If given, dictates the choice of matplotlib GUI backend to use
450 455 (should be one of IPython's supported backends, 'tk', 'qt', 'wx' or
451 456 'gtk'), otherwise we use the default chosen by matplotlib (as
452 457 dictated by the matplotlib build-time options plus the user's
453 458 matplotlibrc configuration file).
454 459 """
455 460 # We want to prevent the loading of pylab to pollute the user's
456 461 # namespace as shown by the %who* magics, so we execute the activation
457 462 # code in an empty namespace, and we update *both* user_ns and
458 463 # user_ns_hidden with this information.
459 464 ns = {}
460 465 gui = pylab_activate(ns, gui, import_all)
461 466 self.user_ns.update(ns)
462 467 self.user_ns_hidden.update(ns)
463 468 # Now we must activate the gui pylab wants to use, and fix %run to take
464 469 # plot updates into account
465 470 enable_gui(gui)
466 471 self.magic_run = self._pylab_magic_run
467 472
468 473 #-------------------------------------------------------------------------
469 474 # Things related to exiting
470 475 #-------------------------------------------------------------------------
471 476
472 477 def ask_exit(self):
473 478 """ Ask the shell to exit. Can be overiden and used as a callback. """
474 479 self.exit_now = True
475 480
476 481 def exit(self):
477 482 """Handle interactive exit.
478 483
479 484 This method calls the ask_exit callback."""
480 485 if self.confirm_exit:
481 486 if self.ask_yes_no('Do you really want to exit ([y]/n)?','y'):
482 487 self.ask_exit()
483 488 else:
484 489 self.ask_exit()
485 490
486 491 #------------------------------------------------------------------------
487 492 # Magic overrides
488 493 #------------------------------------------------------------------------
489 494 # Once the base class stops inheriting from magic, this code needs to be
490 495 # moved into a separate machinery as well. For now, at least isolate here
491 496 # the magics which this class needs to implement differently from the base
492 497 # class, or that are unique to it.
493 498
494 499 def magic_autoindent(self, parameter_s = ''):
495 500 """Toggle autoindent on/off (if available)."""
496 501
497 502 self.shell.set_autoindent()
498 503 print "Automatic indentation is:",['OFF','ON'][self.shell.autoindent]
499 504
500 505 @skip_doctest
501 506 def magic_cpaste(self, parameter_s=''):
502 507 """Paste & execute a pre-formatted code block from clipboard.
503 508
504 509 You must terminate the block with '--' (two minus-signs) or Ctrl-D alone on the
505 510 line. You can also provide your own sentinel with '%paste -s %%' ('%%'
506 511 is the new sentinel for this operation)
507 512
508 513 The block is dedented prior to execution to enable execution of method
509 514 definitions. '>' and '+' characters at the beginning of a line are
510 515 ignored, to allow pasting directly from e-mails, diff files and
511 516 doctests (the '...' continuation prompt is also stripped). The
512 517 executed block is also assigned to variable named 'pasted_block' for
513 518 later editing with '%edit pasted_block'.
514 519
515 520 You can also pass a variable name as an argument, e.g. '%cpaste foo'.
516 521 This assigns the pasted block to variable 'foo' as string, without
517 522 dedenting or executing it (preceding >>> and + is still stripped)
518 523
519 524 '%cpaste -r' re-executes the block previously entered by cpaste.
520 525
521 526 Do not be alarmed by garbled output on Windows (it's a readline bug).
522 527 Just press enter and type -- (and press enter again) and the block
523 528 will be what was just pasted.
524 529
525 530 IPython statements (magics, shell escapes) are not supported (yet).
526 531
527 532 See also
528 533 --------
529 534 paste: automatically pull code from clipboard.
530 535
531 536 Examples
532 537 --------
533 538 ::
534 539
535 540 In [8]: %cpaste
536 541 Pasting code; enter '--' alone on the line to stop.
537 542 :>>> a = ["world!", "Hello"]
538 543 :>>> print " ".join(sorted(a))
539 544 :--
540 545 Hello world!
541 546 """
542 547
543 548 opts,args = self.parse_options(parameter_s,'rs:',mode='string')
544 549 par = args.strip()
545 550 if opts.has_key('r'):
546 551 self._rerun_pasted()
547 552 return
548 553
549 554 sentinel = opts.get('s','--')
550 555
551 556 block = self._strip_pasted_lines_for_code(
552 557 self._get_pasted_lines(sentinel))
553 558
554 559 self._execute_block(block, par)
555 560
556 561 def magic_paste(self, parameter_s=''):
557 562 """Paste & execute a pre-formatted code block from clipboard.
558 563
559 564 The text is pulled directly from the clipboard without user
560 565 intervention and printed back on the screen before execution (unless
561 566 the -q flag is given to force quiet mode).
562 567
563 568 The block is dedented prior to execution to enable execution of method
564 569 definitions. '>' and '+' characters at the beginning of a line are
565 570 ignored, to allow pasting directly from e-mails, diff files and
566 571 doctests (the '...' continuation prompt is also stripped). The
567 572 executed block is also assigned to variable named 'pasted_block' for
568 573 later editing with '%edit pasted_block'.
569 574
570 575 You can also pass a variable name as an argument, e.g. '%paste foo'.
571 576 This assigns the pasted block to variable 'foo' as string, without
572 577 dedenting or executing it (preceding >>> and + is still stripped)
573 578
574 579 Options
575 580 -------
576 581
577 582 -r: re-executes the block previously entered by cpaste.
578 583
579 584 -q: quiet mode: do not echo the pasted text back to the terminal.
580 585
581 586 IPython statements (magics, shell escapes) are not supported (yet).
582 587
583 588 See also
584 589 --------
585 590 cpaste: manually paste code into terminal until you mark its end.
586 591 """
587 592 opts,args = self.parse_options(parameter_s,'rq',mode='string')
588 593 par = args.strip()
589 594 if opts.has_key('r'):
590 595 self._rerun_pasted()
591 596 return
592 597
593 598 text = self.shell.hooks.clipboard_get()
594 599 block = self._strip_pasted_lines_for_code(text.splitlines())
595 600
596 601 # By default, echo back to terminal unless quiet mode is requested
597 602 if not opts.has_key('q'):
598 603 write = self.shell.write
599 604 write(self.shell.pycolorize(block))
600 605 if not block.endswith('\n'):
601 606 write('\n')
602 607 write("## -- End pasted text --\n")
603 608
604 609 self._execute_block(block, par)
605 610
606 611 def showindentationerror(self):
607 612 super(TerminalInteractiveShell, self).showindentationerror()
608 613 print("If you want to paste code into IPython, try the %paste magic function.")
609 614
610 615
611 616 InteractiveShellABC.register(TerminalInteractiveShell)
@@ -1,136 +1,137 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 34 ip._replace_rlhist_multiline(u'source')
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 45 ip.hlen_before_cell = ip.readline.get_current_history_length()
46 46 ip._replace_rlhist_multiline(u'sourc€\nsource2')
47 47
48 48 self.assertEquals(ip.readline.get_current_history_length(),
49 49 ip.hlen_before_cell)
50 50 hist = self.rl_hist_entries(ip.readline, 2)
51 51 self.assertEquals(hist, ghist)
52 52
53 53 @skipif(not get_ipython().has_readline, 'no readline')
54 54 def test_replace_multiline_hist_adds(self):
55 55 """Test that multiline replace function adds history"""
56 56 ip = get_ipython()
57 57
58 58 ip.hlen_before_cell = ip.readline.get_current_history_length()
59 59 ip._replace_rlhist_multiline(u'sourc€')
60 60
61 61 self.assertEquals(ip.hlen_before_cell,
62 62 ip.readline.get_current_history_length())
63 63
64 64 @skipif(not get_ipython().has_readline, 'no readline')
65 65 def test_replace_multiline_hist_keeps_history(self):
66 66 """Test that multiline replace does not delete history"""
67 67 ip = get_ipython()
68 68 ip.multiline_history = True
69 69
70 70 ghist = [u'line1', u'line2']
71 71 for h in ghist:
72 72 ip.readline.add_history(h)
73 73
74 74 #start cell
75 75 ip.hlen_before_cell = ip.readline.get_current_history_length()
76 # nothing added to rl history, should do nothing
76 77 ip._replace_rlhist_multiline(u'sourc€\nsource2')
77 78
78 79 self.assertEquals(ip.readline.get_current_history_length(),
79 80 ip.hlen_before_cell)
80 hist = self.rl_hist_entries(ip.readline, 3)
81 self.assertEquals(hist, ghist + ['sourc€\nsource2'])
81 hist = self.rl_hist_entries(ip.readline, 2)
82 self.assertEquals(hist, ghist)
82 83
83 84
84 85 @skipif(not get_ipython().has_readline, 'no readline')
85 86 def test_replace_multiline_hist_replaces_twice(self):
86 87 """Test that multiline entries are replaced twice"""
87 88 ip = get_ipython()
88 89 ip.multiline_history = True
89 90
90 91 ip.readline.add_history(u'line0')
91 92 #start cell
92 93 ip.hlen_before_cell = ip.readline.get_current_history_length()
93 94 ip.readline.add_history('l€ne1')
94 95 ip.readline.add_history('line2')
95 96 #replace cell with single line
96 97 ip._replace_rlhist_multiline(u'l€ne1\nline2')
97 98 ip.readline.add_history('l€ne3')
98 99 ip.readline.add_history('line4')
99 100 #replace cell with single line
100 101 ip._replace_rlhist_multiline(u'l€ne3\nline4')
101 102
102 103 self.assertEquals(ip.readline.get_current_history_length(),
103 104 ip.hlen_before_cell)
104 105 hist = self.rl_hist_entries(ip.readline, 3)
105 106 self.assertEquals(hist, ['line0', 'l€ne1\nline2', 'l€ne3\nline4'])
106 107
107 108
108 109 @skipif(not get_ipython().has_readline, 'no readline')
109 110 def test_replace_multiline_hist_replaces_empty_line(self):
110 111 """Test that multiline history skips empty line cells"""
111 112 ip = get_ipython()
112 113 ip.multiline_history = True
113 114
114 115 ip.readline.add_history(u'line0')
115 116 #start cell
116 117 ip.hlen_before_cell = ip.readline.get_current_history_length()
117 118 ip.readline.add_history('l€ne1')
118 119 ip.readline.add_history('line2')
119 120 ip._replace_rlhist_multiline(u'l€ne1\nline2')
120 121 ip.readline.add_history('')
121 122 ip._replace_rlhist_multiline(u'')
122 123 ip.readline.add_history('l€ne3')
123 124 ip._replace_rlhist_multiline(u'l€ne3')
124 125 ip.readline.add_history(' ')
125 126 ip._replace_rlhist_multiline(' ')
126 127 ip.readline.add_history('\t')
127 128 ip.readline.add_history('\t ')
128 129 ip._replace_rlhist_multiline('\t')
129 130 ip.readline.add_history('line4')
130 131 ip._replace_rlhist_multiline(u'line4')
131 132
132 133 self.assertEquals(ip.readline.get_current_history_length(),
133 134 ip.hlen_before_cell)
134 135 hist = self.rl_hist_entries(ip.readline, 4)
135 136 # expect no empty cells in history
136 137 self.assertEquals(hist, ['line0', 'l€ne1\nline2', 'l€ne3', 'line4'])
General Comments 0
You need to be logged in to leave comments. Login now