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

42 statements  

1# Implements https://sw.kovidgoyal.net/kitty/graphics-protocol/ 

2 

3from base64 import b64encode, b64decode 

4import sys 

5from typing import Union 

6 

7def _supports_kitty_graphics() -> bool: 

8 import platform 

9 

10 if platform.system() not in ("Darwin", "Linux"): 

11 return False 

12 

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 

29 

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 

43 

44 

45supports_kitty_graphics = _supports_kitty_graphics() 

46 

47 

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) 

65 

66 

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) 

73 

74 

75display_formatter_default_active_types = [ 

76 "text/plain", 

77 *(["image/png"] if supports_kitty_graphics else []), 

78] 

79 

80terminal_default_mime_renderers = { 

81 "image/png": kitty_png_render, 

82}