##// END OF EJS Templates
Update documentation, and prepare inine matplotlib.
Matthias Bussonnier -
Show More
@@ -0,0 +1,60 b''
1
2 .. _shell_mimerenderer:
3
4
5 Mime Renderer Extensions
6 ========================
7
8 Like it's cousins, Jupyter Notebooks and JupyterLab, Terminal IPython can be
9 thought to render a number of mimetypes in the shell. This can be used to either
10 display inline images if your terminal emulator supports it; or open some
11 display results with external file viewers.
12
13 Registering new mimetype handlers can so far only be done my extensions and
14 requires 4 steps:
15
16 - Define a callable that takes 2 parameters:``data`` and ``metadata``; return
17 value of the callable is so far ignored. This callable is responsible for
18 "displaying" the given mimetype. Which can be sending the right escape
19 sequences and bytes to the current terminal; or open an external program. -
20 - Appending the right mimetype to ``ipython.display_formatter.active_types``
21 for IPython to know it should not ignore those mimetypes.
22 - Enabling the given mimetype: ``ipython.display_formatter.formatters[mime].enabled = True``
23 - Registering above callable with mimetype handler:
24 ``ipython.mime_renderers[mime] = handler``
25
26
27 Here is a complete IPython extension to display images inline and convert math
28 to png, before displaying it inline for iterm2 on macOS ::
29
30
31 from base64 import encodebytes
32 from IPython.lib.latextools import latex_to_png
33
34
35 def mathcat(data, meta):
36 png = latex_to_png(f'$${data}$$'.replace('\displaystyle', '').replace('$$$', '$$'))
37 imcat(png, meta)
38
39 IMAGE_CODE = '\033]1337;File=name=name;inline=true;:{}\a'
40
41 def imcat(image_data, metadata):
42 try:
43 print(IMAGE_CODE.format(encodebytes(image_data).decode()))
44 # bug workaround
45 except:
46 print(IMAGE_CODE.format(image_data))
47
48 def register_mimerenderer(ipython, mime, handler):
49 ipython.display_formatter.active_types.append(mime)
50 ipython.display_formatter.formatters[mime].enabled = True
51 ipython.mime_renderers[mime] = handler
52
53 def load_ipython_extension(ipython):
54 register_mimerenderer(ipython, 'image/png', imcat)
55 register_mimerenderer(ipython, 'image/jpeg', imcat)
56 register_mimerenderer(ipython, 'text/latex', mathcat)
57
58 This example only work for iterm2 on macOS and skip error handling for brevity.
59 One could also invoke an external viewer with ``subporcess.run()`` and a
60 temporary file, which is left as an exercise.
@@ -1,558 +1,558 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 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 (
14 14 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
15 15 Any, validate
16 16 )
17 17
18 18 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
19 19 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
20 20 from prompt_toolkit.formatted_text import PygmentsTokens
21 21 from prompt_toolkit.history import InMemoryHistory
22 22 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
23 23 from prompt_toolkit.output import ColorDepth
24 24 from prompt_toolkit.patch_stdout import patch_stdout
25 25 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
26 26 from prompt_toolkit.styles import DynamicStyle, merge_styles
27 27 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
28 28
29 29 from pygments.styles import get_style_by_name
30 30 from pygments.style import Style
31 31 from pygments.token import Token
32 32
33 33 from .debugger import TerminalPdb, Pdb
34 34 from .magics import TerminalMagics
35 35 from .pt_inputhooks import get_inputhook_name_and_func
36 36 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
37 37 from .ptutils import IPythonPTCompleter, IPythonPTLexer
38 38 from .shortcuts import create_ipython_shortcuts
39 39
40 40 DISPLAY_BANNER_DEPRECATED = object()
41 41
42 42
43 43 class _NoStyle(Style): pass
44 44
45 45
46 46
47 47 _style_overrides_light_bg = {
48 48 Token.Prompt: '#0000ff',
49 49 Token.PromptNum: '#0000ee bold',
50 50 Token.OutPrompt: '#cc0000',
51 51 Token.OutPromptNum: '#bb0000 bold',
52 52 }
53 53
54 54 _style_overrides_linux = {
55 55 Token.Prompt: '#00cc00',
56 56 Token.PromptNum: '#00bb00 bold',
57 57 Token.OutPrompt: '#cc0000',
58 58 Token.OutPromptNum: '#bb0000 bold',
59 59 }
60 60
61 61 def get_default_editor():
62 62 try:
63 63 return os.environ['EDITOR']
64 64 except KeyError:
65 65 pass
66 66 except UnicodeError:
67 67 warn("$EDITOR environment variable is not pure ASCII. Using platform "
68 68 "default editor.")
69 69
70 70 if os.name == 'posix':
71 71 return 'vi' # the only one guaranteed to be there!
72 72 else:
73 73 return 'notepad' # same in Windows!
74 74
75 75 # conservatively check for tty
76 76 # overridden streams can result in things like:
77 77 # - sys.stdin = None
78 78 # - no isatty method
79 79 for _name in ('stdin', 'stdout', 'stderr'):
80 80 _stream = getattr(sys, _name)
81 81 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
82 82 _is_tty = False
83 83 break
84 84 else:
85 85 _is_tty = True
86 86
87 87
88 88 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
89 89
90 90 class TerminalInteractiveShell(InteractiveShell):
91 91 mime_renderers = Dict().tag(config=True)
92 92
93 93 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
94 94 'to reserve for the completion menu'
95 95 ).tag(config=True)
96 96
97 97 pt_app = None
98 98 debugger_history = None
99 99
100 100 simple_prompt = Bool(_use_simple_prompt,
101 101 help="""Use `raw_input` for the REPL, without completion and prompt colors.
102 102
103 103 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
104 104 IPython own testing machinery, and emacs inferior-shell integration through elpy.
105 105
106 106 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
107 107 environment variable is set, or the current terminal is not a tty."""
108 108 ).tag(config=True)
109 109
110 110 @property
111 111 def debugger_cls(self):
112 112 return Pdb if self.simple_prompt else TerminalPdb
113 113
114 114 confirm_exit = Bool(True,
115 115 help="""
116 116 Set to confirm when you try to exit IPython with an EOF (Control-D
117 117 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
118 118 you can force a direct exit without any confirmation.""",
119 119 ).tag(config=True)
120 120
121 121 editing_mode = Unicode('emacs',
122 122 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
123 123 ).tag(config=True)
124 124
125 125 mouse_support = Bool(False,
126 126 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
127 127 ).tag(config=True)
128 128
129 129 # We don't load the list of styles for the help string, because loading
130 130 # Pygments plugins takes time and can cause unexpected errors.
131 131 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
132 132 help="""The name or class of a Pygments style to use for syntax
133 133 highlighting. To see available styles, run `pygmentize -L styles`."""
134 134 ).tag(config=True)
135 135
136 136 @validate('editing_mode')
137 137 def _validate_editing_mode(self, proposal):
138 138 if proposal['value'].lower() == 'vim':
139 139 proposal['value']= 'vi'
140 140 elif proposal['value'].lower() == 'default':
141 141 proposal['value']= 'emacs'
142 142
143 143 if hasattr(EditingMode, proposal['value'].upper()):
144 144 return proposal['value'].lower()
145 145
146 146 return self.editing_mode
147 147
148 148
149 149 @observe('editing_mode')
150 150 def _editing_mode(self, change):
151 151 u_mode = change.new.upper()
152 152 if self.pt_app:
153 153 self.pt_app.editing_mode = u_mode
154 154
155 155 @observe('highlighting_style')
156 156 @observe('colors')
157 157 def _highlighting_style_changed(self, change):
158 158 self.refresh_style()
159 159
160 160 def refresh_style(self):
161 161 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
162 162
163 163
164 164 highlighting_style_overrides = Dict(
165 165 help="Override highlighting format for specific tokens"
166 166 ).tag(config=True)
167 167
168 168 true_color = Bool(False,
169 169 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
170 170 "If your terminal supports true color, the following command "
171 171 "should print 'TRUECOLOR' in orange: "
172 172 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
173 173 ).tag(config=True)
174 174
175 175 editor = Unicode(get_default_editor(),
176 176 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
177 177 ).tag(config=True)
178 178
179 179 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
180 180
181 181 prompts = Instance(Prompts)
182 182
183 183 @default('prompts')
184 184 def _prompts_default(self):
185 185 return self.prompts_class(self)
186 186
187 187 # @observe('prompts')
188 188 # def _(self, change):
189 189 # self._update_layout()
190 190
191 191 @default('displayhook_class')
192 192 def _displayhook_class_default(self):
193 193 return RichPromptDisplayHook
194 194
195 195 term_title = Bool(True,
196 196 help="Automatically set the terminal title"
197 197 ).tag(config=True)
198 198
199 199 term_title_format = Unicode("IPython: {cwd}",
200 200 help="Customize the terminal title format. This is a python format string. " +
201 201 "Available substitutions are: {cwd}."
202 202 ).tag(config=True)
203 203
204 204 display_completions = Enum(('column', 'multicolumn','readlinelike'),
205 205 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
206 206 "'readlinelike'. These options are for `prompt_toolkit`, see "
207 207 "`prompt_toolkit` documentation for more information."
208 208 ),
209 209 default_value='multicolumn').tag(config=True)
210 210
211 211 highlight_matching_brackets = Bool(True,
212 212 help="Highlight matching brackets.",
213 213 ).tag(config=True)
214 214
215 215 extra_open_editor_shortcuts = Bool(False,
216 216 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
217 217 "This is in addition to the F2 binding, which is always enabled."
218 218 ).tag(config=True)
219 219
220 220 handle_return = Any(None,
221 221 help="Provide an alternative handler to be called when the user presses "
222 222 "Return. This is an advanced option intended for debugging, which "
223 223 "may be changed or removed in later releases."
224 224 ).tag(config=True)
225 225
226 226 enable_history_search = Bool(True,
227 227 help="Allows to enable/disable the prompt toolkit history search"
228 228 ).tag(config=True)
229 229
230 230 prompt_includes_vi_mode = Bool(True,
231 231 help="Display the current vi mode (when using vi editing mode)."
232 232 ).tag(config=True)
233 233
234 234 @observe('term_title')
235 235 def init_term_title(self, change=None):
236 236 # Enable or disable the terminal title.
237 237 if self.term_title:
238 238 toggle_set_term_title(True)
239 239 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
240 240 else:
241 241 toggle_set_term_title(False)
242 242
243 243 def init_display_formatter(self):
244 244 super(TerminalInteractiveShell, self).init_display_formatter()
245 245 # terminal only supports plain text
246 246 self.display_formatter.active_types = ['text/plain']
247 247 # disable `_ipython_display_`
248 248 self.display_formatter.ipython_display_formatter.enabled = False
249 249
250 250 def init_prompt_toolkit_cli(self):
251 251 if self.simple_prompt:
252 252 # Fall back to plain non-interactive output for tests.
253 253 # This is very limited.
254 254 def prompt():
255 255 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
256 256 lines = [input(prompt_text)]
257 257 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
258 258 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
259 259 lines.append( input(prompt_continuation) )
260 260 return '\n'.join(lines)
261 261 self.prompt_for_code = prompt
262 262 return
263 263
264 264 # Set up keyboard shortcuts
265 265 key_bindings = create_ipython_shortcuts(self)
266 266
267 267 # Pre-populate history from IPython's history database
268 268 history = InMemoryHistory()
269 269 last_cell = u""
270 270 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
271 271 include_latest=True):
272 272 # Ignore blank lines and consecutive duplicates
273 273 cell = cell.rstrip()
274 274 if cell and (cell != last_cell):
275 275 history.append_string(cell)
276 276 last_cell = cell
277 277
278 278 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
279 279 self.style = DynamicStyle(lambda: self._style)
280 280
281 281 editing_mode = getattr(EditingMode, self.editing_mode.upper())
282 282
283 283 self.pt_app = PromptSession(
284 284 editing_mode=editing_mode,
285 285 key_bindings=key_bindings,
286 286 history=history,
287 287 completer=IPythonPTCompleter(shell=self),
288 288 enable_history_search = self.enable_history_search,
289 289 style=self.style,
290 290 include_default_pygments_style=False,
291 291 mouse_support=self.mouse_support,
292 292 enable_open_in_editor=self.extra_open_editor_shortcuts,
293 293 color_depth=self.color_depth,
294 294 **self._extra_prompt_options())
295 295
296 296 def _make_style_from_name_or_cls(self, name_or_cls):
297 297 """
298 298 Small wrapper that make an IPython compatible style from a style name
299 299
300 300 We need that to add style for prompt ... etc.
301 301 """
302 302 style_overrides = {}
303 303 if name_or_cls == 'legacy':
304 304 legacy = self.colors.lower()
305 305 if legacy == 'linux':
306 306 style_cls = get_style_by_name('monokai')
307 307 style_overrides = _style_overrides_linux
308 308 elif legacy == 'lightbg':
309 309 style_overrides = _style_overrides_light_bg
310 310 style_cls = get_style_by_name('pastie')
311 311 elif legacy == 'neutral':
312 312 # The default theme needs to be visible on both a dark background
313 313 # and a light background, because we can't tell what the terminal
314 314 # looks like. These tweaks to the default theme help with that.
315 315 style_cls = get_style_by_name('default')
316 316 style_overrides.update({
317 317 Token.Number: '#007700',
318 318 Token.Operator: 'noinherit',
319 319 Token.String: '#BB6622',
320 320 Token.Name.Function: '#2080D0',
321 321 Token.Name.Class: 'bold #2080D0',
322 322 Token.Name.Namespace: 'bold #2080D0',
323 323 Token.Prompt: '#009900',
324 324 Token.PromptNum: '#ansibrightgreen bold',
325 325 Token.OutPrompt: '#990000',
326 326 Token.OutPromptNum: '#ansibrightred bold',
327 327 })
328 328
329 329 # Hack: Due to limited color support on the Windows console
330 330 # the prompt colors will be wrong without this
331 331 if os.name == 'nt':
332 332 style_overrides.update({
333 333 Token.Prompt: '#ansidarkgreen',
334 334 Token.PromptNum: '#ansigreen bold',
335 335 Token.OutPrompt: '#ansidarkred',
336 336 Token.OutPromptNum: '#ansired bold',
337 337 })
338 338 elif legacy =='nocolor':
339 339 style_cls=_NoStyle
340 340 style_overrides = {}
341 341 else :
342 342 raise ValueError('Got unknown colors: ', legacy)
343 343 else :
344 344 if isinstance(name_or_cls, str):
345 345 style_cls = get_style_by_name(name_or_cls)
346 346 else:
347 347 style_cls = name_or_cls
348 348 style_overrides = {
349 349 Token.Prompt: '#009900',
350 350 Token.PromptNum: '#ansibrightgreen bold',
351 351 Token.OutPrompt: '#990000',
352 352 Token.OutPromptNum: '#ansibrightred bold',
353 353 }
354 354 style_overrides.update(self.highlighting_style_overrides)
355 355 style = merge_styles([
356 356 style_from_pygments_cls(style_cls),
357 357 style_from_pygments_dict(style_overrides),
358 358 ])
359 359
360 360 return style
361 361
362 362 @property
363 363 def pt_complete_style(self):
364 364 return {
365 365 'multicolumn': CompleteStyle.MULTI_COLUMN,
366 366 'column': CompleteStyle.COLUMN,
367 367 'readlinelike': CompleteStyle.READLINE_LIKE,
368 368 }[self.display_completions]
369 369
370 370 @property
371 371 def color_depth(self):
372 372 return (ColorDepth.TRUE_COLOR if self.true_color else None)
373 373
374 374 def _extra_prompt_options(self):
375 375 """
376 376 Return the current layout option for the current Terminal InteractiveShell
377 377 """
378 378 def get_message():
379 379 return PygmentsTokens(self.prompts.in_prompt_tokens())
380 380
381 381 return {
382 382 'complete_in_thread': False,
383 383 'lexer':IPythonPTLexer(),
384 384 'reserve_space_for_menu':self.space_for_menu,
385 385 'message': get_message,
386 386 'prompt_continuation': (
387 387 lambda width, lineno, is_soft_wrap:
388 388 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
389 389 'multiline': True,
390 390 'complete_style': self.pt_complete_style,
391 391
392 392 # Highlight matching brackets, but only when this setting is
393 393 # enabled, and only when the DEFAULT_BUFFER has the focus.
394 394 'input_processors': [ConditionalProcessor(
395 395 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
396 396 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
397 397 Condition(lambda: self.highlight_matching_brackets))],
398 398 'inputhook': self.inputhook,
399 399 }
400 400
401 401 def prompt_for_code(self):
402 402 if self.rl_next_input:
403 403 default = self.rl_next_input
404 404 self.rl_next_input = None
405 405 else:
406 406 default = ''
407 407
408 408 with patch_stdout(raw=True):
409 409 text = self.pt_app.prompt(
410 410 default=default,
411 411 # pre_run=self.pre_prompt,# reset_current_buffer=True,
412 412 **self._extra_prompt_options())
413 413 return text
414 414
415 415 def enable_win_unicode_console(self):
416 416 if sys.version_info >= (3, 6):
417 417 # Since PEP 528, Python uses the unicode APIs for the Windows
418 418 # console by default, so WUC shouldn't be needed.
419 419 return
420 420
421 421 import win_unicode_console
422 422 win_unicode_console.enable()
423 423
424 424 def init_io(self):
425 425 if sys.platform not in {'win32', 'cli'}:
426 426 return
427 427
428 428 self.enable_win_unicode_console()
429 429
430 430 import colorama
431 431 colorama.init()
432 432
433 433 # For some reason we make these wrappers around stdout/stderr.
434 434 # For now, we need to reset them so all output gets coloured.
435 435 # https://github.com/ipython/ipython/issues/8669
436 436 # io.std* are deprecated, but don't show our own deprecation warnings
437 437 # during initialization of the deprecated API.
438 438 with warnings.catch_warnings():
439 439 warnings.simplefilter('ignore', DeprecationWarning)
440 440 io.stdout = io.IOStream(sys.stdout)
441 441 io.stderr = io.IOStream(sys.stderr)
442 442
443 443 def init_magics(self):
444 444 super(TerminalInteractiveShell, self).init_magics()
445 445 self.register_magics(TerminalMagics)
446 446
447 447 def init_alias(self):
448 448 # The parent class defines aliases that can be safely used with any
449 449 # frontend.
450 450 super(TerminalInteractiveShell, self).init_alias()
451 451
452 452 # Now define aliases that only make sense on the terminal, because they
453 453 # need direct access to the console in a way that we can't emulate in
454 454 # GUI or web frontend
455 455 if os.name == 'posix':
456 456 for cmd in ('clear', 'more', 'less', 'man'):
457 457 self.alias_manager.soft_define_alias(cmd, cmd)
458 458
459 459
460 460 def __init__(self, *args, **kwargs):
461 461 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
462 462 self.init_prompt_toolkit_cli()
463 463 self.init_term_title()
464 464 self.keep_running = True
465 465
466 466 self.debugger_history = InMemoryHistory()
467 467
468 468 def ask_exit(self):
469 469 self.keep_running = False
470 470
471 471 rl_next_input = None
472 472
473 473 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
474 474
475 475 if display_banner is not DISPLAY_BANNER_DEPRECATED:
476 476 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
477 477
478 478 self.keep_running = True
479 479 while self.keep_running:
480 480 print(self.separate_in, end='')
481 481
482 482 try:
483 483 code = self.prompt_for_code()
484 484 except EOFError:
485 485 if (not self.confirm_exit) \
486 486 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
487 487 self.ask_exit()
488 488
489 489 else:
490 490 if code:
491 491 self.run_cell(code, store_history=True)
492 492
493 493 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
494 494 # An extra layer of protection in case someone mashing Ctrl-C breaks
495 495 # out of our internal code.
496 496 if display_banner is not DISPLAY_BANNER_DEPRECATED:
497 497 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
498 498 while True:
499 499 try:
500 500 self.interact()
501 501 break
502 502 except KeyboardInterrupt as e:
503 503 print("\n%s escaped interact()\n" % type(e).__name__)
504 504 finally:
505 505 # An interrupt during the eventloop will mess up the
506 506 # internal state of the prompt_toolkit library.
507 507 # Stopping the eventloop fixes this, see
508 508 # https://github.com/ipython/ipython/pull/9867
509 509 if hasattr(self, '_eventloop'):
510 510 self._eventloop.stop()
511 511
512 512 _inputhook = None
513 513 def inputhook(self, context):
514 514 if self._inputhook is not None:
515 515 self._inputhook(context)
516 516
517 517 active_eventloop = None
518 518 def enable_gui(self, gui=None):
519 if gui:
519 if gui and (gui != 'inline') :
520 520 self.active_eventloop, self._inputhook =\
521 521 get_inputhook_name_and_func(gui)
522 522 else:
523 523 self.active_eventloop = self._inputhook = None
524 524
525 525 # Run !system commands directly, not through pipes, so terminal programs
526 526 # work correctly.
527 527 system = InteractiveShell.system_raw
528 528
529 529 def auto_rewrite_input(self, cmd):
530 530 """Overridden from the parent class to use fancy rewriting prompt"""
531 531 if not self.show_rewritten_input:
532 532 return
533 533
534 534 tokens = self.prompts.rewrite_prompt_tokens()
535 535 if self.pt_app:
536 536 print_formatted_text(PygmentsTokens(tokens), end='',
537 537 style=self.pt_app.app.style)
538 538 print(cmd)
539 539 else:
540 540 prompt = ''.join(s for t, s in tokens)
541 541 print(prompt, cmd, sep='')
542 542
543 543 _prompts_before = None
544 544 def switch_doctest_mode(self, mode):
545 545 """Switch prompts to classic for %doctest_mode"""
546 546 if mode:
547 547 self._prompts_before = self.prompts
548 548 self.prompts = ClassicPrompts(self)
549 549 elif self._prompts_before:
550 550 self.prompts = self._prompts_before
551 551 self._prompts_before = None
552 552 # self._update_layout()
553 553
554 554
555 555 InteractiveShellABC.register(TerminalInteractiveShell)
556 556
557 557 if __name__ == '__main__':
558 558 TerminalInteractiveShell.instance().interact()
@@ -1,49 +1,49 b''
1 1 import importlib
2 2 import os
3 3
4 4 aliases = {
5 5 'qt4': 'qt',
6 6 'gtk2': 'gtk',
7 7 }
8 8
9 9 backends = [
10 10 'qt', 'qt4', 'qt5',
11 11 'gtk', 'gtk2', 'gtk3',
12 12 'tk',
13 13 'wx',
14 14 'pyglet', 'glut',
15 'osx',
15 'osx'
16 16 ]
17 17
18 18 registered = {}
19 19
20 20 def register(name, inputhook):
21 21 """Register the function *inputhook* as an event loop integration."""
22 22 registered[name] = inputhook
23 23
24 24 class UnknownBackend(KeyError):
25 25 def __init__(self, name):
26 26 self.name = name
27 27
28 28 def __str__(self):
29 29 return ("No event loop integration for {!r}. "
30 30 "Supported event loops are: {}").format(self.name,
31 31 ', '.join(backends + sorted(registered)))
32 32
33 33 def get_inputhook_name_and_func(gui):
34 34 if gui in registered:
35 35 return gui, registered[gui]
36 36
37 37 if gui not in backends:
38 38 raise UnknownBackend(gui)
39 39
40 40 if gui in aliases:
41 41 return get_inputhook_name_and_func(aliases[gui])
42 42
43 43 gui_mod = gui
44 44 if gui == 'qt5':
45 45 os.environ['QT_API'] = 'pyqt5'
46 46 gui_mod = 'qt'
47 47
48 48 mod = importlib.import_module('IPython.terminal.pt_inputhooks.'+gui_mod)
49 49 return gui, mod.inputhook
@@ -1,34 +1,35 b''
1 1 .. _config_index:
2 2
3 3 ===============================
4 4 Configuration and customization
5 5 ===============================
6 6
7 7 Configuring IPython
8 8 -------------------
9 9
10 10 .. toctree::
11 11 :maxdepth: 2
12 12
13 13 intro
14 14 options/index
15 15 shortcuts/index
16 16 details
17 17
18 18 .. seealso::
19 19
20 20 :doc:`/development/config`
21 21 Technical details of the config system.
22 22
23 23 Extending and integrating with IPython
24 24 --------------------------------------
25 25
26 26 .. toctree::
27 27 :maxdepth: 2
28 28
29 29 extensions/index
30 30 integrating
31 31 custommagics
32 shell_mimerenderer
32 33 inputtransforms
33 34 callbacks
34 35 eventloops
@@ -1,55 +1,19 b''
1 1 Arbitrary Mimetypes Handing in Terminal
2 2 =======================================
3 3
4 4 When using IPython terminal it is now possible to register function to handle
5 arbitrary mimetypes (``TerminalInteractiveShell.mime_renderers`` ``Dict``
6 configurable). While rendering non-text based representation was possible in
5 arbitrary mimetypes. While rendering non-text based representation was possible in
7 6 many jupyter frontend; it was not possible in terminal IPython, as usually
8 7 terminal are limited to displaying text. As many terminal these days provide
9 8 escape sequences to display non-text; bringing this loved feature to IPython CLI
10 9 made a lot of sens. This functionality will not only allow inline images; but
11 allow opening of external program; for example ``fmplayer`` to "display" sound
10 allow opening of external program; for example ``mplayer`` to "display" sound
12 11 files.
13 12
14 Here is a complete IPython tension to display images inline and convert math to
15 png, before displaying it inline ::
16
17
18 from base64 import encodebytes
19 from IPython.lib.latextools import latex_to_png
20
21
22 def mathcat(data, meta):
23 png = latex_to_png(f'$${data}$$'.replace('\displaystyle', '').replace('$$$', '$$'))
24 imcat(png, meta)
25
26 IMAGE_CODE = '\033]1337;File=name=name;inline=true;:{}\a'
27
28 def imcat(image_data, metadata):
29 try:
30 print(IMAGE_CODE.format(encodebytes(image_data).decode()))
31 # bug workaround
32 except:
33 print(IMAGE_CODE.format(image_data))
34
35 def register_mimerenderer(ipython, mime, handler):
36 ipython.display_formatter.active_types.append(mime)
37 ipython.display_formatter.formatters[mime].enabled = True
38 ipython.mime_renderers[mime] = handler
39
40 def load_ipython_extension(ipython):
41 register_mimerenderer(ipython, 'image/png', imcat)
42 register_mimerenderer(ipython, 'image/jpeg', imcat)
43 register_mimerenderer(ipython, 'text/latex', mathcat)
44
45 This example only work for iterm2 on mac os and skip error handling for brevity.
46 One could also invoke an external viewer with ``subporcess.run()`` and a
47 tempfile, which is left as an exercise.
48
49 13 So far only the hooks necessary for this are in place, but no default mime
50 14 renderers added; so inline images will only be available via extensions. We will
51 15 progressively enable these features by default in the next few releases, and
52 16 contribution is welcomed.
53 17
54
55
18 We welcome any feedback on the API. See :ref:`shell_mimerenderer` for more
19 informations.
General Comments 0
You need to be logged in to leave comments. Login now