Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/IPython/core/kitty.py: 33%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Implements https://sw.kovidgoyal.net/kitty/graphics-protocol/
3from base64 import b64encode, b64decode
4import sys
5from typing import Union
7def _supports_kitty_graphics() -> bool:
8 import platform
10 if platform.system() not in ("Darwin", "Linux"):
11 return False
13 isatty = getattr(sys.stdout, "isatty", None)
14 if not callable(isatty) or not isatty():
15 return False
16 # Hardcoding process names instead of using
17 # https://sw.kovidgoyal.net/kitty/graphics-protocol/#querying-support-and-available-transmission-mediums
18 # to avoid startup slowdown
19 supported_terminals = {
20 "ghostty",
21 "iTerm2",
22 "kitty",
23 "konsole",
24 "warp",
25 "wayst",
26 "wezterm-gui",
27 }
28 import psutil
30 try:
31 process = psutil.Process()
32 while process := process.parent():
33 if process.name() in supported_terminals:
34 return True
35 except (psutil.Error, OSError):
36 # Walking the process tree can fail when /proc is mounted with
37 # ``hidepid`` on shared multi-user systems (common on HPC clusters):
38 # ancestor processes owned by other users are inaccessible and psutil
39 # raises AccessDenied. Treat as "unsupported" rather than letting it
40 # abort the import of IPython.
41 return False
42 return False
45supports_kitty_graphics = _supports_kitty_graphics()
48def png_to_kitty_ansi(png: bytes) -> str:
49 if not png.startswith(b"\x89PNG\r\n\x1a\n"):
50 raise ValueError
51 # This simplicity resembles
52 # https://sw.kovidgoyal.net/kitty/graphics-protocol/#a-minimal-example
53 # but if we need tmux support, we can switch to Unicode like
54 # https://github.com/hzeller/timg/blob/main/src/kitty-canvas.cc
55 result = ["\033_Ga=T,f=100,", "m=1;"]
56 encoded = b64encode(png)
57 for i in range(0, len(encoded), 4096):
58 result.append(encoded[i : i + 4096].decode("ascii"))
59 result.append("\033\\")
60 result.append("\033_G")
61 result.append("m=1;")
62 del result[-2:]
63 result[-3] = "m=0;"
64 return "".join(result)
67def kitty_png_render(png: Union[bytes, str], _md_dict: object) -> None:
68 if isinstance(png, str):
69 png = png_to_kitty_ansi(b64decode(png))
70 else:
71 png = png_to_kitty_ansi(png)
72 print(png)
75display_formatter_default_active_types = [
76 "text/plain",
77 *(["image/png"] if supports_kitty_graphics else []),
78]
80terminal_default_mime_renderers = {
81 "image/png": kitty_png_render,
82}