##// END OF EJS Templates
disable `_ipython_display_` in terminal IPython...
Min RK -
Show More
@@ -1,489 +1,491 b''
1 1 """IPython terminal interface using prompt_toolkit"""
2 2
3 3 import os
4 4 import sys
5 5 import warnings
6 6 from warnings import warn
7 7
8 8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
9 9 from IPython.utils import io
10 10 from IPython.utils.py3compat import cast_unicode_py2, input
11 11 from IPython.utils.terminal import toggle_set_term_title, set_term_title
12 12 from IPython.utils.process import abbrev_cwd
13 13 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union
14 14
15 15 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
16 16 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
17 17 from prompt_toolkit.history import InMemoryHistory
18 18 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
19 19 from prompt_toolkit.interface import CommandLineInterface
20 20 from prompt_toolkit.key_binding.manager import KeyBindingManager
21 21 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
22 22 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
23 23
24 24 from pygments.styles import get_style_by_name, get_all_styles
25 25 from pygments.style import Style
26 26 from pygments.token import Token
27 27
28 28 from .debugger import TerminalPdb, Pdb
29 29 from .magics import TerminalMagics
30 30 from .pt_inputhooks import get_inputhook_name_and_func
31 31 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
32 32 from .ptutils import IPythonPTCompleter, IPythonPTLexer
33 33 from .shortcuts import register_ipython_shortcuts
34 34
35 35 DISPLAY_BANNER_DEPRECATED = object()
36 36
37 37
38 38 from pygments.style import Style
39 39
40 40 class _NoStyle(Style): pass
41 41
42 42
43 43
44 44 _style_overrides_light_bg = {
45 45 Token.Prompt: '#0000ff',
46 46 Token.PromptNum: '#0000ee bold',
47 47 Token.OutPrompt: '#cc0000',
48 48 Token.OutPromptNum: '#bb0000 bold',
49 49 }
50 50
51 51 _style_overrides_linux = {
52 52 Token.Prompt: '#00cc00',
53 53 Token.PromptNum: '#00bb00 bold',
54 54 Token.OutPrompt: '#cc0000',
55 55 Token.OutPromptNum: '#bb0000 bold',
56 56 }
57 57
58 58
59 59
60 60 def get_default_editor():
61 61 try:
62 62 return os.environ['EDITOR']
63 63 except KeyError:
64 64 pass
65 65 except UnicodeError:
66 66 warn("$EDITOR environment variable is not pure ASCII. Using platform "
67 67 "default editor.")
68 68
69 69 if os.name == 'posix':
70 70 return 'vi' # the only one guaranteed to be there!
71 71 else:
72 72 return 'notepad' # same in Windows!
73 73
74 74 # conservatively check for tty
75 75 # overridden streams can result in things like:
76 76 # - sys.stdin = None
77 77 # - no isatty method
78 78 for _name in ('stdin', 'stdout', 'stderr'):
79 79 _stream = getattr(sys, _name)
80 80 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
81 81 _is_tty = False
82 82 break
83 83 else:
84 84 _is_tty = True
85 85
86 86
87 87 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
88 88
89 89 class TerminalInteractiveShell(InteractiveShell):
90 90 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
91 91 'to reserve for the completion menu'
92 92 ).tag(config=True)
93 93
94 94 def _space_for_menu_changed(self, old, new):
95 95 self._update_layout()
96 96
97 97 pt_cli = None
98 98 debugger_history = None
99 99 _pt_app = None
100 100
101 101 simple_prompt = Bool(_use_simple_prompt,
102 102 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
103 103
104 104 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
105 105 IPython own testing machinery, and emacs inferior-shell integration through elpy.
106 106
107 107 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
108 108 environment variable is set, or the current terminal is not a tty.
109 109
110 110 """
111 111 ).tag(config=True)
112 112
113 113 @property
114 114 def debugger_cls(self):
115 115 return Pdb if self.simple_prompt else TerminalPdb
116 116
117 117 confirm_exit = Bool(True,
118 118 help="""
119 119 Set to confirm when you try to exit IPython with an EOF (Control-D
120 120 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
121 121 you can force a direct exit without any confirmation.""",
122 122 ).tag(config=True)
123 123
124 124 editing_mode = Unicode('emacs',
125 125 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
126 126 ).tag(config=True)
127 127
128 128 mouse_support = Bool(False,
129 129 help="Enable mouse support in the prompt"
130 130 ).tag(config=True)
131 131
132 132 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
133 133 help="""The name or class of a Pygments style to use for syntax
134 134 highlighting: \n %s""" % ', '.join(get_all_styles())
135 135 ).tag(config=True)
136 136
137 137
138 138 @observe('highlighting_style')
139 139 @observe('colors')
140 140 def _highlighting_style_changed(self, change):
141 141 self.refresh_style()
142 142
143 143 def refresh_style(self):
144 144 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
145 145
146 146
147 147 highlighting_style_overrides = Dict(
148 148 help="Override highlighting format for specific tokens"
149 149 ).tag(config=True)
150 150
151 151 true_color = Bool(False,
152 152 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
153 153 "If your terminal supports true color, the following command "
154 154 "should print 'TRUECOLOR' in orange: "
155 155 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
156 156 ).tag(config=True)
157 157
158 158 editor = Unicode(get_default_editor(),
159 159 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
160 160 ).tag(config=True)
161 161
162 162 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
163 163
164 164 prompts = Instance(Prompts)
165 165
166 166 @default('prompts')
167 167 def _prompts_default(self):
168 168 return self.prompts_class(self)
169 169
170 170 @observe('prompts')
171 171 def _(self, change):
172 172 self._update_layout()
173 173
174 174 @default('displayhook_class')
175 175 def _displayhook_class_default(self):
176 176 return RichPromptDisplayHook
177 177
178 178 term_title = Bool(True,
179 179 help="Automatically set the terminal title"
180 180 ).tag(config=True)
181 181
182 182 display_completions = Enum(('column', 'multicolumn','readlinelike'),
183 183 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
184 184 "'readlinelike'. These options are for `prompt_toolkit`, see "
185 185 "`prompt_toolkit` documentation for more information."
186 186 ),
187 187 default_value='multicolumn').tag(config=True)
188 188
189 189 highlight_matching_brackets = Bool(True,
190 190 help="Highlight matching brackets .",
191 191 ).tag(config=True)
192 192
193 193 @observe('term_title')
194 194 def init_term_title(self, change=None):
195 195 # Enable or disable the terminal title.
196 196 if self.term_title:
197 197 toggle_set_term_title(True)
198 198 set_term_title('IPython: ' + abbrev_cwd())
199 199 else:
200 200 toggle_set_term_title(False)
201 201
202 202 def init_display_formatter(self):
203 203 super(TerminalInteractiveShell, self).init_display_formatter()
204 204 # terminal only supports plain text
205 205 self.display_formatter.active_types = ['text/plain']
206 # disable `_ipython_display_`
207 self.display_formatter.ipython_display_formatter.enabled = False
206 208
207 209 def init_prompt_toolkit_cli(self):
208 210 if self.simple_prompt:
209 211 # Fall back to plain non-interactive output for tests.
210 212 # This is very limited, and only accepts a single line.
211 213 def prompt():
212 214 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
213 215 self.prompt_for_code = prompt
214 216 return
215 217
216 218 # Set up keyboard shortcuts
217 219 kbmanager = KeyBindingManager.for_prompt()
218 220 register_ipython_shortcuts(kbmanager.registry, self)
219 221
220 222 # Pre-populate history from IPython's history database
221 223 history = InMemoryHistory()
222 224 last_cell = u""
223 225 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
224 226 include_latest=True):
225 227 # Ignore blank lines and consecutive duplicates
226 228 cell = cell.rstrip()
227 229 if cell and (cell != last_cell):
228 230 history.append(cell)
229 231 last_cell = cell
230 232
231 233 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
232 234 style = DynamicStyle(lambda: self._style)
233 235
234 236 editing_mode = getattr(EditingMode, self.editing_mode.upper())
235 237
236 238 def patch_stdout(**kwargs):
237 239 return self.pt_cli.patch_stdout_context(**kwargs)
238 240
239 241 self._pt_app = create_prompt_application(
240 242 editing_mode=editing_mode,
241 243 key_bindings_registry=kbmanager.registry,
242 244 history=history,
243 245 completer=IPythonPTCompleter(shell=self,
244 246 patch_stdout=patch_stdout),
245 247 enable_history_search=True,
246 248 style=style,
247 249 mouse_support=self.mouse_support,
248 250 **self._layout_options()
249 251 )
250 252 self._eventloop = create_eventloop(self.inputhook)
251 253 self.pt_cli = CommandLineInterface(
252 254 self._pt_app, eventloop=self._eventloop,
253 255 output=create_output(true_color=self.true_color))
254 256
255 257 def _make_style_from_name_or_cls(self, name_or_cls):
256 258 """
257 259 Small wrapper that make an IPython compatible style from a style name
258 260
259 261 We need that to add style for prompt ... etc.
260 262 """
261 263 style_overrides = {}
262 264 if name_or_cls == 'legacy':
263 265 legacy = self.colors.lower()
264 266 if legacy == 'linux':
265 267 style_cls = get_style_by_name('monokai')
266 268 style_overrides = _style_overrides_linux
267 269 elif legacy == 'lightbg':
268 270 style_overrides = _style_overrides_light_bg
269 271 style_cls = get_style_by_name('pastie')
270 272 elif legacy == 'neutral':
271 273 # The default theme needs to be visible on both a dark background
272 274 # and a light background, because we can't tell what the terminal
273 275 # looks like. These tweaks to the default theme help with that.
274 276 style_cls = get_style_by_name('default')
275 277 style_overrides.update({
276 278 Token.Number: '#007700',
277 279 Token.Operator: 'noinherit',
278 280 Token.String: '#BB6622',
279 281 Token.Name.Function: '#2080D0',
280 282 Token.Name.Class: 'bold #2080D0',
281 283 Token.Name.Namespace: 'bold #2080D0',
282 284 Token.Prompt: '#009900',
283 285 Token.PromptNum: '#00ff00 bold',
284 286 Token.OutPrompt: '#990000',
285 287 Token.OutPromptNum: '#ff0000 bold',
286 288 })
287 289 elif legacy =='nocolor':
288 290 style_cls=_NoStyle
289 291 style_overrides = {}
290 292 else :
291 293 raise ValueError('Got unknown colors: ', legacy)
292 294 else :
293 295 if isinstance(name_or_cls, str):
294 296 style_cls = get_style_by_name(name_or_cls)
295 297 else:
296 298 style_cls = name_or_cls
297 299 style_overrides = {
298 300 Token.Prompt: '#009900',
299 301 Token.PromptNum: '#00ff00 bold',
300 302 Token.OutPrompt: '#990000',
301 303 Token.OutPromptNum: '#ff0000 bold',
302 304 }
303 305 style_overrides.update(self.highlighting_style_overrides)
304 306 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
305 307 style_dict=style_overrides)
306 308
307 309 return style
308 310
309 311 def _layout_options(self):
310 312 """
311 313 Return the current layout option for the current Terminal InteractiveShell
312 314 """
313 315 return {
314 316 'lexer':IPythonPTLexer(),
315 317 'reserve_space_for_menu':self.space_for_menu,
316 318 'get_prompt_tokens':self.prompts.in_prompt_tokens,
317 319 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
318 320 'multiline':True,
319 321 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
320 322
321 323 # Highlight matching brackets, but only when this setting is
322 324 # enabled, and only when the DEFAULT_BUFFER has the focus.
323 325 'extra_input_processors': [ConditionalProcessor(
324 326 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
325 327 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
326 328 Condition(lambda cli: self.highlight_matching_brackets))],
327 329 }
328 330
329 331 def _update_layout(self):
330 332 """
331 333 Ask for a re computation of the application layout, if for example ,
332 334 some configuration options have changed.
333 335 """
334 336 if self._pt_app:
335 337 self._pt_app.layout = create_prompt_layout(**self._layout_options())
336 338
337 339 def prompt_for_code(self):
338 340 document = self.pt_cli.run(
339 341 pre_run=self.pre_prompt, reset_current_buffer=True)
340 342 return document.text
341 343
342 344 def enable_win_unicode_console(self):
343 345 if sys.version_info >= (3, 6):
344 346 # Since PEP 528, Python uses the unicode APIs for the Windows
345 347 # console by default, so WUC shouldn't be needed.
346 348 return
347 349
348 350 import win_unicode_console
349 351 win_unicode_console.enable()
350 352
351 353 def init_io(self):
352 354 if sys.platform not in {'win32', 'cli'}:
353 355 return
354 356
355 357 self.enable_win_unicode_console()
356 358
357 359 import colorama
358 360 colorama.init()
359 361
360 362 # For some reason we make these wrappers around stdout/stderr.
361 363 # For now, we need to reset them so all output gets coloured.
362 364 # https://github.com/ipython/ipython/issues/8669
363 365 # io.std* are deprecated, but don't show our own deprecation warnings
364 366 # during initialization of the deprecated API.
365 367 with warnings.catch_warnings():
366 368 warnings.simplefilter('ignore', DeprecationWarning)
367 369 io.stdout = io.IOStream(sys.stdout)
368 370 io.stderr = io.IOStream(sys.stderr)
369 371
370 372 def init_magics(self):
371 373 super(TerminalInteractiveShell, self).init_magics()
372 374 self.register_magics(TerminalMagics)
373 375
374 376 def init_alias(self):
375 377 # The parent class defines aliases that can be safely used with any
376 378 # frontend.
377 379 super(TerminalInteractiveShell, self).init_alias()
378 380
379 381 # Now define aliases that only make sense on the terminal, because they
380 382 # need direct access to the console in a way that we can't emulate in
381 383 # GUI or web frontend
382 384 if os.name == 'posix':
383 385 for cmd in ['clear', 'more', 'less', 'man']:
384 386 self.alias_manager.soft_define_alias(cmd, cmd)
385 387
386 388
387 389 def __init__(self, *args, **kwargs):
388 390 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
389 391 self.init_prompt_toolkit_cli()
390 392 self.init_term_title()
391 393 self.keep_running = True
392 394
393 395 self.debugger_history = InMemoryHistory()
394 396
395 397 def ask_exit(self):
396 398 self.keep_running = False
397 399
398 400 rl_next_input = None
399 401
400 402 def pre_prompt(self):
401 403 if self.rl_next_input:
402 404 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
403 405 self.rl_next_input = None
404 406
405 407 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
406 408
407 409 if display_banner is not DISPLAY_BANNER_DEPRECATED:
408 410 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
409 411
410 412 self.keep_running = True
411 413 while self.keep_running:
412 414 print(self.separate_in, end='')
413 415
414 416 try:
415 417 code = self.prompt_for_code()
416 418 except EOFError:
417 419 if (not self.confirm_exit) \
418 420 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
419 421 self.ask_exit()
420 422
421 423 else:
422 424 if code:
423 425 self.run_cell(code, store_history=True)
424 426
425 427 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
426 428 # An extra layer of protection in case someone mashing Ctrl-C breaks
427 429 # out of our internal code.
428 430 if display_banner is not DISPLAY_BANNER_DEPRECATED:
429 431 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
430 432 while True:
431 433 try:
432 434 self.interact()
433 435 break
434 436 except KeyboardInterrupt as e:
435 437 print("\n%s escaped interact()\n" % type(e).__name__)
436 438 finally:
437 439 # An interrupt during the eventloop will mess up the
438 440 # internal state of the prompt_toolkit library.
439 441 # Stopping the eventloop fixes this, see
440 442 # https://github.com/ipython/ipython/pull/9867
441 443 if hasattr(self, '_eventloop'):
442 444 self._eventloop.stop()
443 445
444 446 _inputhook = None
445 447 def inputhook(self, context):
446 448 if self._inputhook is not None:
447 449 self._inputhook(context)
448 450
449 451 active_eventloop = None
450 452 def enable_gui(self, gui=None):
451 453 if gui:
452 454 self.active_eventloop, self._inputhook =\
453 455 get_inputhook_name_and_func(gui)
454 456 else:
455 457 self.active_eventloop = self._inputhook = None
456 458
457 459 # Run !system commands directly, not through pipes, so terminal programs
458 460 # work correctly.
459 461 system = InteractiveShell.system_raw
460 462
461 463 def auto_rewrite_input(self, cmd):
462 464 """Overridden from the parent class to use fancy rewriting prompt"""
463 465 if not self.show_rewritten_input:
464 466 return
465 467
466 468 tokens = self.prompts.rewrite_prompt_tokens()
467 469 if self.pt_cli:
468 470 self.pt_cli.print_tokens(tokens)
469 471 print(cmd)
470 472 else:
471 473 prompt = ''.join(s for t, s in tokens)
472 474 print(prompt, cmd, sep='')
473 475
474 476 _prompts_before = None
475 477 def switch_doctest_mode(self, mode):
476 478 """Switch prompts to classic for %doctest_mode"""
477 479 if mode:
478 480 self._prompts_before = self.prompts
479 481 self.prompts = ClassicPrompts(self)
480 482 elif self._prompts_before:
481 483 self.prompts = self._prompts_before
482 484 self._prompts_before = None
483 485 self._update_layout()
484 486
485 487
486 488 InteractiveShellABC.register(TerminalInteractiveShell)
487 489
488 490 if __name__ == '__main__':
489 491 TerminalInteractiveShell.instance().interact()
@@ -1,129 +1,153 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for the TerminalInteractiveShell and related pieces."""
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2011 The IPython Development Team
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
9 5
10 6 import sys
11 7 import unittest
12 8
13 9 from IPython.core.inputtransformer import InputTransformer
14 10 from IPython.testing import tools as tt
11 from IPython.utils.capture import capture_output
15 12
16 13 # Decorator for interaction loop tests -----------------------------------------
17 14
18 15 class mock_input_helper(object):
19 16 """Machinery for tests of the main interact loop.
20 17
21 18 Used by the mock_input decorator.
22 19 """
23 20 def __init__(self, testgen):
24 21 self.testgen = testgen
25 22 self.exception = None
26 23 self.ip = get_ipython()
27 24
28 25 def __enter__(self):
29 26 self.orig_prompt_for_code = self.ip.prompt_for_code
30 27 self.ip.prompt_for_code = self.fake_input
31 28 return self
32 29
33 30 def __exit__(self, etype, value, tb):
34 31 self.ip.prompt_for_code = self.orig_prompt_for_code
35 32
36 33 def fake_input(self):
37 34 try:
38 35 return next(self.testgen)
39 36 except StopIteration:
40 37 self.ip.keep_running = False
41 38 return u''
42 39 except:
43 40 self.exception = sys.exc_info()
44 41 self.ip.keep_running = False
45 42 return u''
46 43
47 44 def mock_input(testfunc):
48 45 """Decorator for tests of the main interact loop.
49 46
50 47 Write the test as a generator, yield-ing the input strings, which IPython
51 48 will see as if they were typed in at the prompt.
52 49 """
53 50 def test_method(self):
54 51 testgen = testfunc(self)
55 52 with mock_input_helper(testgen) as mih:
56 53 mih.ip.interact()
57 54
58 55 if mih.exception is not None:
59 56 # Re-raise captured exception
60 57 etype, value, tb = mih.exception
61 58 import traceback
62 59 traceback.print_tb(tb, file=sys.stdout)
63 60 del tb # Avoid reference loop
64 61 raise value
65 62
66 63 return test_method
67 64
68 65 # Test classes -----------------------------------------------------------------
69 66
70 67 class InteractiveShellTestCase(unittest.TestCase):
71 68 def rl_hist_entries(self, rl, n):
72 69 """Get last n readline history entries as a list"""
73 70 return [rl.get_history_item(rl.get_current_history_length() - x)
74 71 for x in range(n - 1, -1, -1)]
75 72
76 73 @mock_input
77 74 def test_inputtransformer_syntaxerror(self):
78 75 ip = get_ipython()
79 76 transformer = SyntaxErrorTransformer()
80 77 ip.input_splitter.python_line_transforms.append(transformer)
81 78 ip.input_transformer_manager.python_line_transforms.append(transformer)
82 79
83 80 try:
84 81 #raise Exception
85 82 with tt.AssertPrints('4', suppress=False):
86 83 yield u'print(2*2)'
87 84
88 85 with tt.AssertPrints('SyntaxError: input contains', suppress=False):
89 86 yield u'print(2345) # syntaxerror'
90 87
91 88 with tt.AssertPrints('16', suppress=False):
92 89 yield u'print(4*4)'
93 90
94 91 finally:
95 92 ip.input_splitter.python_line_transforms.remove(transformer)
96 93 ip.input_transformer_manager.python_line_transforms.remove(transformer)
97 94
98 95 def test_plain_text_only(self):
99 96 ip = get_ipython()
100 97 formatter = ip.display_formatter
101 98 assert formatter.active_types == ['text/plain']
99 assert not formatter.ipython_display_formatter.enabled
100
101 class Test(object):
102 def __repr__(self):
103 return "<Test %i>" % id(self)
104
105 def _repr_html_(self):
106 return '<html>'
107
108 # verify that HTML repr isn't computed
109 obj = Test()
110 data, _ = formatter.format(obj)
111 self.assertEqual(data, {'text/plain': repr(obj)})
112
113 class Test2(Test):
114 def _ipython_display_(self):
115 from IPython.display import display
116 display('<custom>')
117
118 # verify that _ipython_display_ shortcut isn't called
119 obj = Test2()
120 with capture_output() as captured:
121 data, _ = formatter.format(obj)
122
123 self.assertEqual(data, {'text/plain': repr(obj)})
124 assert captured.stdout == ''
125
102 126
103 127
104 128 class SyntaxErrorTransformer(InputTransformer):
105 129 def push(self, line):
106 130 pos = line.find('syntaxerror')
107 131 if pos >= 0:
108 132 e = SyntaxError('input contains "syntaxerror"')
109 133 e.text = line
110 134 e.offset = pos + 1
111 135 raise e
112 136 return line
113 137
114 138 def reset(self):
115 139 pass
116 140
117 141 class TerminalMagicsTestCase(unittest.TestCase):
118 142 def test_paste_magics_blankline(self):
119 143 """Test that code with a blank line doesn't get split (gh-3246)."""
120 144 ip = get_ipython()
121 145 s = ('def pasted_func(a):\n'
122 146 ' b = a+1\n'
123 147 '\n'
124 148 ' return b')
125 149
126 150 tm = ip.magics_manager.registry['TerminalMagics']
127 151 tm.store_or_execute(s, name=None)
128 152
129 153 self.assertEqual(ip.user_ns['pasted_func'](54), 55)
General Comments 0
You need to be logged in to leave comments. Login now