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

64 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:35 +0000

1import sys 

2import time 

3from typing import TYPE_CHECKING, Callable, Dict, Iterable, List, Union 

4 

5if sys.version_info >= (3, 8): 

6 from typing import Final 

7else: 

8 from typing_extensions import Final # pragma: no cover 

9 

10from .segment import ControlCode, ControlType, Segment 

11 

12if TYPE_CHECKING: 

13 from .console import Console, ConsoleOptions, RenderResult 

14 

15STRIP_CONTROL_CODES: Final = [ 

16 7, # Bell 

17 8, # Backspace 

18 11, # Vertical tab 

19 12, # Form feed 

20 13, # Carriage return 

21] 

22_CONTROL_STRIP_TRANSLATE: Final = { 

23 _codepoint: None for _codepoint in STRIP_CONTROL_CODES 

24} 

25 

26CONTROL_ESCAPE: Final = { 

27 7: "\\a", 

28 8: "\\b", 

29 11: "\\v", 

30 12: "\\f", 

31 13: "\\r", 

32} 

33 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

51} 

52 

53 

54class Control: 

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

56 

57 Args: 

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

59 tuple of ControlType and an integer parameter 

60 """ 

61 

62 __slots__ = ["segment"] 

63 

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

65 control_codes: List[ControlCode] = [ 

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

67 ] 

68 _format_map = CONTROL_CODES_FORMAT 

69 rendered_codes = "".join( 

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

71 ) 

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

73 

74 @classmethod 

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

76 """Ring the 'bell'.""" 

77 return cls(ControlType.BELL) 

78 

79 @classmethod 

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

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

82 return cls(ControlType.HOME) 

83 

84 @classmethod 

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

86 """Move cursor relative to current position. 

87 

88 Args: 

89 x (int): X offset. 

90 y (int): Y offset. 

91 

92 Returns: 

93 ~Control: Control object. 

94 

95 """ 

96 

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

98 control = ControlType 

99 if x: 

100 yield ( 

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

102 abs(x), 

103 ) 

104 if y: 

105 yield ( 

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

107 abs(y), 

108 ) 

109 

110 control = cls(*get_codes()) 

111 return control 

112 

113 @classmethod 

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

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

116 

117 Returns: 

118 x (int): absolute x (column) 

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

120 

121 Returns: 

122 ~Control: Control object. 

123 """ 

124 

125 return ( 

126 cls( 

127 (ControlType.CURSOR_MOVE_TO_COLUMN, x), 

128 ( 

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

130 abs(y), 

131 ), 

132 ) 

133 if y 

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

135 ) 

136 

137 @classmethod 

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

139 """Move cursor to absolute position. 

140 

141 Args: 

142 x (int): x offset (column) 

143 y (int): y offset (row) 

144 

145 Returns: 

146 ~Control: Control object. 

147 """ 

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

149 

150 @classmethod 

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

152 """Clear the screen.""" 

153 return cls(ControlType.CLEAR) 

154 

155 @classmethod 

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

157 """Show or hide the cursor.""" 

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

159 

160 @classmethod 

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

162 """Enable or disable alt screen.""" 

163 if enable: 

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

165 else: 

166 return cls(ControlType.DISABLE_ALT_SCREEN) 

167 

168 @classmethod 

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

170 """Set the terminal window title 

171 

172 Args: 

173 title (str): The new terminal window title 

174 """ 

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

176 

177 def __str__(self) -> str: 

178 return self.segment.text 

179 

180 def __rich_console__( 

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

182 ) -> "RenderResult": 

183 if self.segment.text: 

184 yield self.segment 

185 

186 

187def strip_control_codes( 

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

189) -> str: 

190 """Remove control codes from text. 

191 

192 Args: 

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

194 

195 Returns: 

196 str: String with control codes removed. 

197 """ 

198 return text.translate(_translate_table) 

199 

200 

201def escape_control_codes( 

202 text: str, 

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

204) -> str: 

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

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

207 

208 Args: 

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

210 

211 Returns: 

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

213 """ 

214 return text.translate(_translate_table) 

215 

216 

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

218 from rich.console import Console 

219 

220 console = Console() 

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

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

223 for i in range(10): 

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

225 time.sleep(0.5)