Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/rich/control.py: 56%

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

61 statements  

1import time 

2from typing import TYPE_CHECKING, Callable, Dict, Iterable, List, Union, Final 

3 

4from .segment import ControlCode, ControlType, Segment 

5 

6if TYPE_CHECKING: 

7 from .console import Console, ConsoleOptions, RenderResult 

8 

9STRIP_CONTROL_CODES: Final = [ 

10 7, # Bell 

11 8, # Backspace 

12 11, # Vertical tab 

13 12, # Form feed 

14 13, # Carriage return 

15] 

16_CONTROL_STRIP_TRANSLATE: Final = { 

17 _codepoint: None for _codepoint in STRIP_CONTROL_CODES 

18} 

19 

20CONTROL_ESCAPE: Final = { 

21 7: "\\a", 

22 8: "\\b", 

23 11: "\\v", 

24 12: "\\f", 

25 13: "\\r", 

26} 

27 

28CONTROL_CODES_FORMAT: Dict[int, Callable[..., str]] = { 

29 ControlType.BELL: lambda: "\x07", 

30 ControlType.CARRIAGE_RETURN: lambda: "\r", 

31 ControlType.HOME: lambda: "\x1b[H", 

32 ControlType.CLEAR: lambda: "\x1b[2J", 

33 ControlType.ENABLE_ALT_SCREEN: lambda: "\x1b[?1049h", 

34 ControlType.DISABLE_ALT_SCREEN: lambda: "\x1b[?1049l", 

35 ControlType.SHOW_CURSOR: lambda: "\x1b[?25h", 

36 ControlType.HIDE_CURSOR: lambda: "\x1b[?25l", 

37 ControlType.CURSOR_UP: lambda param: f"\x1b[{param}A", 

38 ControlType.CURSOR_DOWN: lambda param: f"\x1b[{param}B", 

39 ControlType.CURSOR_FORWARD: lambda param: f"\x1b[{param}C", 

40 ControlType.CURSOR_BACKWARD: lambda param: f"\x1b[{param}D", 

41 ControlType.CURSOR_MOVE_TO_COLUMN: lambda param: f"\x1b[{param+1}G", 

42 ControlType.ERASE_IN_LINE: lambda param: f"\x1b[{param}K", 

43 ControlType.CURSOR_MOVE_TO: lambda x, y: f"\x1b[{y+1};{x+1}H", 

44 ControlType.SET_WINDOW_TITLE: lambda title: f"\x1b]0;{title}\x07", 

45} 

46 

47 

48class Control: 

49 """A renderable that inserts a control code (non printable but may move cursor). 

50 

51 Args: 

52 *codes (str): Positional arguments are either a :class:`~rich.segment.ControlType` enum or a 

53 tuple of ControlType and an integer parameter 

54 """ 

55 

56 __slots__ = ["segment"] 

57 

58 def __init__(self, *codes: Union[ControlType, ControlCode]) -> None: 

59 control_codes: List[ControlCode] = [ 

60 (code,) if isinstance(code, ControlType) else code for code in codes 

61 ] 

62 _format_map = CONTROL_CODES_FORMAT 

63 rendered_codes = "".join( 

64 _format_map[code](*parameters) for code, *parameters in control_codes 

65 ) 

66 self.segment = Segment(rendered_codes, None, control_codes) 

67 

68 @classmethod 

69 def bell(cls) -> "Control": 

70 """Ring the 'bell'.""" 

71 return cls(ControlType.BELL) 

72 

73 @classmethod 

74 def home(cls) -> "Control": 

75 """Move cursor to 'home' position.""" 

76 return cls(ControlType.HOME) 

77 

78 @classmethod 

79 def move(cls, x: int = 0, y: int = 0) -> "Control": 

80 """Move cursor relative to current position. 

81 

82 Args: 

83 x (int): X offset. 

84 y (int): Y offset. 

85 

86 Returns: 

87 ~Control: Control object. 

88 

89 """ 

90 

91 def get_codes() -> Iterable[ControlCode]: 

92 control = ControlType 

93 if x: 

94 yield ( 

95 control.CURSOR_FORWARD if x > 0 else control.CURSOR_BACKWARD, 

96 abs(x), 

97 ) 

98 if y: 

99 yield ( 

100 control.CURSOR_DOWN if y > 0 else control.CURSOR_UP, 

101 abs(y), 

102 ) 

103 

104 control = cls(*get_codes()) 

105 return control 

106 

107 @classmethod 

108 def move_to_column(cls, x: int, y: int = 0) -> "Control": 

109 """Move to the given column, optionally add offset to row. 

110 

111 Returns: 

112 x (int): absolute x (column) 

113 y (int): optional y offset (row) 

114 

115 Returns: 

116 ~Control: Control object. 

117 """ 

118 

119 return ( 

120 cls( 

121 (ControlType.CURSOR_MOVE_TO_COLUMN, x), 

122 ( 

123 ControlType.CURSOR_DOWN if y > 0 else ControlType.CURSOR_UP, 

124 abs(y), 

125 ), 

126 ) 

127 if y 

128 else cls((ControlType.CURSOR_MOVE_TO_COLUMN, x)) 

129 ) 

130 

131 @classmethod 

132 def move_to(cls, x: int, y: int) -> "Control": 

133 """Move cursor to absolute position. 

134 

135 Args: 

136 x (int): x offset (column) 

137 y (int): y offset (row) 

138 

139 Returns: 

140 ~Control: Control object. 

141 """ 

142 return cls((ControlType.CURSOR_MOVE_TO, x, y)) 

143 

144 @classmethod 

145 def clear(cls) -> "Control": 

146 """Clear the screen.""" 

147 return cls(ControlType.CLEAR) 

148 

149 @classmethod 

150 def show_cursor(cls, show: bool) -> "Control": 

151 """Show or hide the cursor.""" 

152 return cls(ControlType.SHOW_CURSOR if show else ControlType.HIDE_CURSOR) 

153 

154 @classmethod 

155 def alt_screen(cls, enable: bool) -> "Control": 

156 """Enable or disable alt screen.""" 

157 if enable: 

158 return cls(ControlType.ENABLE_ALT_SCREEN, ControlType.HOME) 

159 else: 

160 return cls(ControlType.DISABLE_ALT_SCREEN) 

161 

162 @classmethod 

163 def title(cls, title: str) -> "Control": 

164 """Set the terminal window title 

165 

166 Args: 

167 title (str): The new terminal window title 

168 """ 

169 return cls((ControlType.SET_WINDOW_TITLE, title)) 

170 

171 def __str__(self) -> str: 

172 return self.segment.text 

173 

174 def __rich_console__( 

175 self, console: "Console", options: "ConsoleOptions" 

176 ) -> "RenderResult": 

177 if self.segment.text: 

178 yield self.segment 

179 

180 

181def strip_control_codes( 

182 text: str, _translate_table: Dict[int, None] = _CONTROL_STRIP_TRANSLATE 

183) -> str: 

184 """Remove control codes from text. 

185 

186 Args: 

187 text (str): A string possibly contain control codes. 

188 

189 Returns: 

190 str: String with control codes removed. 

191 """ 

192 return text.translate(_translate_table) 

193 

194 

195def escape_control_codes( 

196 text: str, 

197 _translate_table: Dict[int, str] = CONTROL_ESCAPE, 

198) -> str: 

199 """Replace control codes with their "escaped" equivalent in the given text. 

200 (e.g. "\b" becomes "\\b") 

201 

202 Args: 

203 text (str): A string possibly containing control codes. 

204 

205 Returns: 

206 str: String with control codes replaced with their escaped version. 

207 """ 

208 return text.translate(_translate_table) 

209 

210 

211if __name__ == "__main__": # pragma: no cover 

212 from rich.console import Console 

213 

214 console = Console() 

215 console.print("Look at the title of your terminal window ^") 

216 # console.print(Control((ControlType.SET_WINDOW_TITLE, "Hello, world!"))) 

217 for i in range(10): 

218 console.set_window_title("🚀 Loading" + "." * i) 

219 time.sleep(0.5)