Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/prompt_toolkit/output/vt100.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

327 statements  

1""" 

2Output for vt100 terminals. 

3 

4A lot of thanks, regarding outputting of colors, goes to the Pygments project: 

5(We don't rely on Pygments anymore, because many things are very custom, and 

6everything has been highly optimized.) 

7http://pygments.org/ 

8""" 

9 

10from __future__ import annotations 

11 

12import io 

13import os 

14import sys 

15from typing import Callable, Dict, Hashable, Iterable, Sequence, TextIO, Tuple 

16 

17from prompt_toolkit.cursor_shapes import CursorShape 

18from prompt_toolkit.data_structures import Size 

19from prompt_toolkit.output import Output 

20from prompt_toolkit.styles import ANSI_COLOR_NAMES, Attrs 

21from prompt_toolkit.utils import is_dumb_terminal 

22 

23from .color_depth import ColorDepth 

24from .flush_stdout import flush_stdout 

25 

26__all__ = [ 

27 "Vt100_Output", 

28] 

29 

30 

31FG_ANSI_COLORS = { 

32 "ansidefault": 39, 

33 # Low intensity. 

34 "ansiblack": 30, 

35 "ansired": 31, 

36 "ansigreen": 32, 

37 "ansiyellow": 33, 

38 "ansiblue": 34, 

39 "ansimagenta": 35, 

40 "ansicyan": 36, 

41 "ansigray": 37, 

42 # High intensity. 

43 "ansibrightblack": 90, 

44 "ansibrightred": 91, 

45 "ansibrightgreen": 92, 

46 "ansibrightyellow": 93, 

47 "ansibrightblue": 94, 

48 "ansibrightmagenta": 95, 

49 "ansibrightcyan": 96, 

50 "ansiwhite": 97, 

51} 

52 

53BG_ANSI_COLORS = { 

54 "ansidefault": 49, 

55 # Low intensity. 

56 "ansiblack": 40, 

57 "ansired": 41, 

58 "ansigreen": 42, 

59 "ansiyellow": 43, 

60 "ansiblue": 44, 

61 "ansimagenta": 45, 

62 "ansicyan": 46, 

63 "ansigray": 47, 

64 # High intensity. 

65 "ansibrightblack": 100, 

66 "ansibrightred": 101, 

67 "ansibrightgreen": 102, 

68 "ansibrightyellow": 103, 

69 "ansibrightblue": 104, 

70 "ansibrightmagenta": 105, 

71 "ansibrightcyan": 106, 

72 "ansiwhite": 107, 

73} 

74 

75 

76ANSI_COLORS_TO_RGB = { 

77 "ansidefault": ( 

78 0x00, 

79 0x00, 

80 0x00, 

81 ), # Don't use, 'default' doesn't really have a value. 

82 "ansiblack": (0x00, 0x00, 0x00), 

83 "ansigray": (0xE5, 0xE5, 0xE5), 

84 "ansibrightblack": (0x7F, 0x7F, 0x7F), 

85 "ansiwhite": (0xFF, 0xFF, 0xFF), 

86 # Low intensity. 

87 "ansired": (0xCD, 0x00, 0x00), 

88 "ansigreen": (0x00, 0xCD, 0x00), 

89 "ansiyellow": (0xCD, 0xCD, 0x00), 

90 "ansiblue": (0x00, 0x00, 0xCD), 

91 "ansimagenta": (0xCD, 0x00, 0xCD), 

92 "ansicyan": (0x00, 0xCD, 0xCD), 

93 # High intensity. 

94 "ansibrightred": (0xFF, 0x00, 0x00), 

95 "ansibrightgreen": (0x00, 0xFF, 0x00), 

96 "ansibrightyellow": (0xFF, 0xFF, 0x00), 

97 "ansibrightblue": (0x00, 0x00, 0xFF), 

98 "ansibrightmagenta": (0xFF, 0x00, 0xFF), 

99 "ansibrightcyan": (0x00, 0xFF, 0xFF), 

100} 

101 

102 

103assert set(FG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) 

104assert set(BG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) 

105assert set(ANSI_COLORS_TO_RGB) == set(ANSI_COLOR_NAMES) 

106 

107 

108def _get_closest_ansi_color(r: int, g: int, b: int, exclude: Sequence[str] = ()) -> str: 

109 """ 

110 Find closest ANSI color. Return it by name. 

111 

112 :param r: Red (Between 0 and 255.) 

113 :param g: Green (Between 0 and 255.) 

114 :param b: Blue (Between 0 and 255.) 

115 :param exclude: A tuple of color names to exclude. (E.g. ``('ansired', )``.) 

116 """ 

117 exclude = list(exclude) 

118 

119 # When we have a bit of saturation, avoid the gray-like colors, otherwise, 

120 # too often the distance to the gray color is less. 

121 saturation = abs(r - g) + abs(g - b) + abs(b - r) # Between 0..510 

122 

123 if saturation > 30: 

124 exclude.extend(["ansilightgray", "ansidarkgray", "ansiwhite", "ansiblack"]) 

125 

126 # Take the closest color. 

127 # (Thanks to Pygments for this part.) 

128 distance = 257 * 257 * 3 # "infinity" (>distance from #000000 to #ffffff) 

129 match = "ansidefault" 

130 

131 for name, (r2, g2, b2) in ANSI_COLORS_TO_RGB.items(): 

132 if name != "ansidefault" and name not in exclude: 

133 d = (r - r2) ** 2 + (g - g2) ** 2 + (b - b2) ** 2 

134 

135 if d < distance: 

136 match = name 

137 distance = d 

138 

139 return match 

140 

141 

142_ColorCodeAndName = Tuple[int, str] 

143 

144 

145class _16ColorCache: 

146 """ 

147 Cache which maps (r, g, b) tuples to 16 ansi colors. 

148 

149 :param bg: Cache for background colors, instead of foreground. 

150 """ 

151 

152 def __init__(self, bg: bool = False) -> None: 

153 self.bg = bg 

154 self._cache: dict[Hashable, _ColorCodeAndName] = {} 

155 

156 def get_code( 

157 self, value: tuple[int, int, int], exclude: Sequence[str] = () 

158 ) -> _ColorCodeAndName: 

159 """ 

160 Return a (ansi_code, ansi_name) tuple. (E.g. ``(44, 'ansiblue')``.) for 

161 a given (r,g,b) value. 

162 """ 

163 key: Hashable = (value, tuple(exclude)) 

164 cache = self._cache 

165 

166 if key not in cache: 

167 cache[key] = self._get(value, exclude) 

168 

169 return cache[key] 

170 

171 def _get( 

172 self, value: tuple[int, int, int], exclude: Sequence[str] = () 

173 ) -> _ColorCodeAndName: 

174 r, g, b = value 

175 match = _get_closest_ansi_color(r, g, b, exclude=exclude) 

176 

177 # Turn color name into code. 

178 if self.bg: 

179 code = BG_ANSI_COLORS[match] 

180 else: 

181 code = FG_ANSI_COLORS[match] 

182 

183 return code, match 

184 

185 

186class _256ColorCache(Dict[Tuple[int, int, int], int]): 

187 """ 

188 Cache which maps (r, g, b) tuples to 256 colors. 

189 """ 

190 

191 def __init__(self) -> None: 

192 # Build color table. 

193 colors: list[tuple[int, int, int]] = [] 

194 

195 # colors 0..15: 16 basic colors 

196 colors.append((0x00, 0x00, 0x00)) # 0 

197 colors.append((0xCD, 0x00, 0x00)) # 1 

198 colors.append((0x00, 0xCD, 0x00)) # 2 

199 colors.append((0xCD, 0xCD, 0x00)) # 3 

200 colors.append((0x00, 0x00, 0xEE)) # 4 

201 colors.append((0xCD, 0x00, 0xCD)) # 5 

202 colors.append((0x00, 0xCD, 0xCD)) # 6 

203 colors.append((0xE5, 0xE5, 0xE5)) # 7 

204 colors.append((0x7F, 0x7F, 0x7F)) # 8 

205 colors.append((0xFF, 0x00, 0x00)) # 9 

206 colors.append((0x00, 0xFF, 0x00)) # 10 

207 colors.append((0xFF, 0xFF, 0x00)) # 11 

208 colors.append((0x5C, 0x5C, 0xFF)) # 12 

209 colors.append((0xFF, 0x00, 0xFF)) # 13 

210 colors.append((0x00, 0xFF, 0xFF)) # 14 

211 colors.append((0xFF, 0xFF, 0xFF)) # 15 

212 

213 # colors 16..232: the 6x6x6 color cube 

214 valuerange = (0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF) 

215 

216 for i in range(217): 

217 r = valuerange[(i // 36) % 6] 

218 g = valuerange[(i // 6) % 6] 

219 b = valuerange[i % 6] 

220 colors.append((r, g, b)) 

221 

222 # colors 233..253: grayscale 

223 for i in range(1, 22): 

224 v = 8 + i * 10 

225 colors.append((v, v, v)) 

226 

227 self.colors = colors 

228 

229 def __missing__(self, value: tuple[int, int, int]) -> int: 

230 r, g, b = value 

231 

232 # Find closest color. 

233 # (Thanks to Pygments for this!) 

234 distance = 257 * 257 * 3 # "infinity" (>distance from #000000 to #ffffff) 

235 match = 0 

236 

237 for i, (r2, g2, b2) in enumerate(self.colors): 

238 if i >= 16: # XXX: We ignore the 16 ANSI colors when mapping RGB 

239 # to the 256 colors, because these highly depend on 

240 # the color scheme of the terminal. 

241 d = (r - r2) ** 2 + (g - g2) ** 2 + (b - b2) ** 2 

242 

243 if d < distance: 

244 match = i 

245 distance = d 

246 

247 # Turn color name into code. 

248 self[value] = match 

249 return match 

250 

251 

252_16_fg_colors = _16ColorCache(bg=False) 

253_16_bg_colors = _16ColorCache(bg=True) 

254_256_colors = _256ColorCache() 

255 

256 

257class _EscapeCodeCache(Dict[Attrs, str]): 

258 """ 

259 Cache for VT100 escape codes. It maps 

260 (fgcolor, bgcolor, bold, underline, strike, reverse) tuples to VT100 

261 escape sequences. 

262 

263 :param true_color: When True, use 24bit colors instead of 256 colors. 

264 """ 

265 

266 def __init__(self, color_depth: ColorDepth) -> None: 

267 self.color_depth = color_depth 

268 

269 def __missing__(self, attrs: Attrs) -> str: 

270 ( 

271 fgcolor, 

272 bgcolor, 

273 bold, 

274 underline, 

275 strike, 

276 italic, 

277 blink, 

278 reverse, 

279 hidden, 

280 ) = attrs 

281 parts: list[str] = [] 

282 

283 parts.extend(self._colors_to_code(fgcolor or "", bgcolor or "")) 

284 

285 if bold: 

286 parts.append("1") 

287 if italic: 

288 parts.append("3") 

289 if blink: 

290 parts.append("5") 

291 if underline: 

292 parts.append("4") 

293 if reverse: 

294 parts.append("7") 

295 if hidden: 

296 parts.append("8") 

297 if strike: 

298 parts.append("9") 

299 

300 if parts: 

301 result = "\x1b[0;" + ";".join(parts) + "m" 

302 else: 

303 result = "\x1b[0m" 

304 

305 self[attrs] = result 

306 return result 

307 

308 def _color_name_to_rgb(self, color: str) -> tuple[int, int, int]: 

309 "Turn 'ffffff', into (0xff, 0xff, 0xff)." 

310 try: 

311 rgb = int(color, 16) 

312 except ValueError: 

313 raise 

314 else: 

315 r = (rgb >> 16) & 0xFF 

316 g = (rgb >> 8) & 0xFF 

317 b = rgb & 0xFF 

318 return r, g, b 

319 

320 def _colors_to_code(self, fg_color: str, bg_color: str) -> Iterable[str]: 

321 """ 

322 Return a tuple with the vt100 values that represent this color. 

323 """ 

324 # When requesting ANSI colors only, and both fg/bg color were converted 

325 # to ANSI, ensure that the foreground and background color are not the 

326 # same. (Unless they were explicitly defined to be the same color.) 

327 fg_ansi = "" 

328 

329 def get(color: str, bg: bool) -> list[int]: 

330 nonlocal fg_ansi 

331 

332 table = BG_ANSI_COLORS if bg else FG_ANSI_COLORS 

333 

334 if not color or self.color_depth == ColorDepth.DEPTH_1_BIT: 

335 return [] 

336 

337 # 16 ANSI colors. (Given by name.) 

338 elif color in table: 

339 return [table[color]] 

340 

341 # RGB colors. (Defined as 'ffffff'.) 

342 else: 

343 try: 

344 rgb = self._color_name_to_rgb(color) 

345 except ValueError: 

346 return [] 

347 

348 # When only 16 colors are supported, use that. 

349 if self.color_depth == ColorDepth.DEPTH_4_BIT: 

350 if bg: # Background. 

351 if fg_color != bg_color: 

352 exclude = [fg_ansi] 

353 else: 

354 exclude = [] 

355 code, name = _16_bg_colors.get_code(rgb, exclude=exclude) 

356 return [code] 

357 else: # Foreground. 

358 code, name = _16_fg_colors.get_code(rgb) 

359 fg_ansi = name 

360 return [code] 

361 

362 # True colors. (Only when this feature is enabled.) 

363 elif self.color_depth == ColorDepth.DEPTH_24_BIT: 

364 r, g, b = rgb 

365 return [(48 if bg else 38), 2, r, g, b] 

366 

367 # 256 RGB colors. 

368 else: 

369 return [(48 if bg else 38), 5, _256_colors[rgb]] 

370 

371 result: list[int] = [] 

372 result.extend(get(fg_color, False)) 

373 result.extend(get(bg_color, True)) 

374 

375 return map(str, result) 

376 

377 

378def _get_size(fileno: int) -> tuple[int, int]: 

379 """ 

380 Get the size of this pseudo terminal. 

381 

382 :param fileno: stdout.fileno() 

383 :returns: A (rows, cols) tuple. 

384 """ 

385 size = os.get_terminal_size(fileno) 

386 return size.lines, size.columns 

387 

388 

389class Vt100_Output(Output): 

390 """ 

391 :param get_size: A callable which returns the `Size` of the output terminal. 

392 :param stdout: Any object with has a `write` and `flush` method + an 'encoding' property. 

393 :param term: The terminal environment variable. (xterm, xterm-256color, linux, ...) 

394 :param enable_cpr: When `True` (the default), send "cursor position 

395 request" escape sequences to the output in order to detect the cursor 

396 position. That way, we can properly determine how much space there is 

397 available for the UI (especially for drop down menus) to render. The 

398 `Renderer` will still try to figure out whether the current terminal 

399 does respond to CPR escapes. When `False`, never attempt to send CPR 

400 requests. 

401 """ 

402 

403 # For the error messages. Only display "Output is not a terminal" once per 

404 # file descriptor. 

405 _fds_not_a_terminal: set[int] = set() 

406 

407 def __init__( 

408 self, 

409 stdout: TextIO, 

410 get_size: Callable[[], Size], 

411 term: str | None = None, 

412 default_color_depth: ColorDepth | None = None, 

413 enable_bell: bool = True, 

414 enable_cpr: bool = True, 

415 ) -> None: 

416 assert all(hasattr(stdout, a) for a in ("write", "flush")) 

417 

418 self._buffer: list[str] = [] 

419 self.stdout: TextIO = stdout 

420 self.default_color_depth = default_color_depth 

421 self._get_size = get_size 

422 self.term = term 

423 self.enable_bell = enable_bell 

424 self.enable_cpr = enable_cpr 

425 

426 # Cache for escape codes. 

427 self._escape_code_caches: dict[ColorDepth, _EscapeCodeCache] = { 

428 ColorDepth.DEPTH_1_BIT: _EscapeCodeCache(ColorDepth.DEPTH_1_BIT), 

429 ColorDepth.DEPTH_4_BIT: _EscapeCodeCache(ColorDepth.DEPTH_4_BIT), 

430 ColorDepth.DEPTH_8_BIT: _EscapeCodeCache(ColorDepth.DEPTH_8_BIT), 

431 ColorDepth.DEPTH_24_BIT: _EscapeCodeCache(ColorDepth.DEPTH_24_BIT), 

432 } 

433 

434 # Keep track of whether the cursor shape was ever changed. 

435 # (We don't restore the cursor shape if it was never changed - by 

436 # default, we don't change them.) 

437 self._cursor_shape_changed = False 

438 

439 # Don't hide/show the cursor when this was already done. 

440 # (`None` means that we don't know whether the cursor is visible or 

441 # not.) 

442 self._cursor_visible: bool | None = None 

443 

444 @classmethod 

445 def from_pty( 

446 cls, 

447 stdout: TextIO, 

448 term: str | None = None, 

449 default_color_depth: ColorDepth | None = None, 

450 enable_bell: bool = True, 

451 ) -> Vt100_Output: 

452 """ 

453 Create an Output class from a pseudo terminal. 

454 (This will take the dimensions by reading the pseudo 

455 terminal attributes.) 

456 """ 

457 fd: int | None 

458 # Normally, this requires a real TTY device, but people instantiate 

459 # this class often during unit tests as well. For convenience, we print 

460 # an error message, use standard dimensions, and go on. 

461 try: 

462 fd = stdout.fileno() 

463 except io.UnsupportedOperation: 

464 fd = None 

465 

466 if not stdout.isatty() and (fd is None or fd not in cls._fds_not_a_terminal): 

467 msg = "Warning: Output is not a terminal (fd=%r).\n" 

468 sys.stderr.write(msg % fd) 

469 sys.stderr.flush() 

470 if fd is not None: 

471 cls._fds_not_a_terminal.add(fd) 

472 

473 def get_size() -> Size: 

474 # If terminal (incorrectly) reports its size as 0, pick a 

475 # reasonable default. See 

476 # https://github.com/ipython/ipython/issues/10071 

477 rows, columns = (None, None) 

478 

479 # It is possible that `stdout` is no longer a TTY device at this 

480 # point. In that case we get an `OSError` in the ioctl call in 

481 # `get_size`. See: 

482 # https://github.com/prompt-toolkit/python-prompt-toolkit/pull/1021 

483 try: 

484 rows, columns = _get_size(stdout.fileno()) 

485 except OSError: 

486 pass 

487 return Size(rows=rows or 24, columns=columns or 80) 

488 

489 return cls( 

490 stdout, 

491 get_size, 

492 term=term, 

493 default_color_depth=default_color_depth, 

494 enable_bell=enable_bell, 

495 ) 

496 

497 def get_size(self) -> Size: 

498 return self._get_size() 

499 

500 def fileno(self) -> int: 

501 "Return file descriptor." 

502 return self.stdout.fileno() 

503 

504 def encoding(self) -> str: 

505 "Return encoding used for stdout." 

506 return self.stdout.encoding 

507 

508 def write_raw(self, data: str) -> None: 

509 """ 

510 Write raw data to output. 

511 """ 

512 self._buffer.append(data) 

513 

514 def write(self, data: str) -> None: 

515 """ 

516 Write text to output. 

517 (Removes vt100 escape codes. -- used for safely writing text.) 

518 """ 

519 self._buffer.append(data.replace("\x1b", "?")) 

520 

521 def set_title(self, title: str) -> None: 

522 """ 

523 Set terminal title. 

524 """ 

525 if self.term not in ( 

526 "linux", 

527 "eterm-color", 

528 ): # Not supported by the Linux console. 

529 self.write_raw( 

530 "\x1b]2;{}\x07".format(title.replace("\x1b", "").replace("\x07", "")) 

531 ) 

532 

533 def clear_title(self) -> None: 

534 self.set_title("") 

535 

536 def erase_screen(self) -> None: 

537 """ 

538 Erases the screen with the background color and moves the cursor to 

539 home. 

540 """ 

541 self.write_raw("\x1b[2J") 

542 

543 def enter_alternate_screen(self) -> None: 

544 self.write_raw("\x1b[?1049h\x1b[H") 

545 

546 def quit_alternate_screen(self) -> None: 

547 self.write_raw("\x1b[?1049l") 

548 

549 def enable_mouse_support(self) -> None: 

550 self.write_raw("\x1b[?1000h") 

551 

552 # Enable mouse-drag support. 

553 self.write_raw("\x1b[?1003h") 

554 

555 # Enable urxvt Mouse mode. (For terminals that understand this.) 

556 self.write_raw("\x1b[?1015h") 

557 

558 # Also enable Xterm SGR mouse mode. (For terminals that understand this.) 

559 self.write_raw("\x1b[?1006h") 

560 

561 # Note: E.g. lxterminal understands 1000h, but not the urxvt or sgr 

562 # extensions. 

563 

564 def disable_mouse_support(self) -> None: 

565 self.write_raw("\x1b[?1000l") 

566 self.write_raw("\x1b[?1015l") 

567 self.write_raw("\x1b[?1006l") 

568 self.write_raw("\x1b[?1003l") 

569 

570 def erase_end_of_line(self) -> None: 

571 """ 

572 Erases from the current cursor position to the end of the current line. 

573 """ 

574 self.write_raw("\x1b[K") 

575 

576 def erase_down(self) -> None: 

577 """ 

578 Erases the screen from the current line down to the bottom of the 

579 screen. 

580 """ 

581 self.write_raw("\x1b[J") 

582 

583 def reset_attributes(self) -> None: 

584 self.write_raw("\x1b[0m") 

585 

586 def set_attributes(self, attrs: Attrs, color_depth: ColorDepth) -> None: 

587 """ 

588 Create new style and output. 

589 

590 :param attrs: `Attrs` instance. 

591 """ 

592 # Get current depth. 

593 escape_code_cache = self._escape_code_caches[color_depth] 

594 

595 # Write escape character. 

596 self.write_raw(escape_code_cache[attrs]) 

597 

598 def disable_autowrap(self) -> None: 

599 self.write_raw("\x1b[?7l") 

600 

601 def enable_autowrap(self) -> None: 

602 self.write_raw("\x1b[?7h") 

603 

604 def enable_bracketed_paste(self) -> None: 

605 self.write_raw("\x1b[?2004h") 

606 

607 def disable_bracketed_paste(self) -> None: 

608 self.write_raw("\x1b[?2004l") 

609 

610 def reset_cursor_key_mode(self) -> None: 

611 """ 

612 For vt100 only. 

613 Put the terminal in cursor mode (instead of application mode). 

614 """ 

615 # Put the terminal in cursor mode. (Instead of application mode.) 

616 self.write_raw("\x1b[?1l") 

617 

618 def cursor_goto(self, row: int = 0, column: int = 0) -> None: 

619 """ 

620 Move cursor position. 

621 """ 

622 self.write_raw("\x1b[%i;%iH" % (row, column)) 

623 

624 def cursor_up(self, amount: int) -> None: 

625 if amount == 0: 

626 pass 

627 elif amount == 1: 

628 self.write_raw("\x1b[A") 

629 else: 

630 self.write_raw("\x1b[%iA" % amount) 

631 

632 def cursor_down(self, amount: int) -> None: 

633 if amount == 0: 

634 pass 

635 elif amount == 1: 

636 # Note: Not the same as '\n', '\n' can cause the window content to 

637 # scroll. 

638 self.write_raw("\x1b[B") 

639 else: 

640 self.write_raw("\x1b[%iB" % amount) 

641 

642 def cursor_forward(self, amount: int) -> None: 

643 if amount == 0: 

644 pass 

645 elif amount == 1: 

646 self.write_raw("\x1b[C") 

647 else: 

648 self.write_raw("\x1b[%iC" % amount) 

649 

650 def cursor_backward(self, amount: int) -> None: 

651 if amount == 0: 

652 pass 

653 elif amount == 1: 

654 self.write_raw("\b") # '\x1b[D' 

655 else: 

656 self.write_raw("\x1b[%iD" % amount) 

657 

658 def hide_cursor(self) -> None: 

659 if self._cursor_visible in (True, None): 

660 self._cursor_visible = False 

661 self.write_raw("\x1b[?25l") 

662 

663 def show_cursor(self) -> None: 

664 if self._cursor_visible in (False, None): 

665 self._cursor_visible = True 

666 self.write_raw("\x1b[?12l\x1b[?25h") # Stop blinking cursor and show. 

667 

668 def set_cursor_shape(self, cursor_shape: CursorShape) -> None: 

669 if cursor_shape == CursorShape._NEVER_CHANGE: 

670 return 

671 

672 self._cursor_shape_changed = True 

673 self.write_raw( 

674 { 

675 CursorShape.BLOCK: "\x1b[2 q", 

676 CursorShape.BEAM: "\x1b[6 q", 

677 CursorShape.UNDERLINE: "\x1b[4 q", 

678 CursorShape.BLINKING_BLOCK: "\x1b[1 q", 

679 CursorShape.BLINKING_BEAM: "\x1b[5 q", 

680 CursorShape.BLINKING_UNDERLINE: "\x1b[3 q", 

681 }.get(cursor_shape, "") 

682 ) 

683 

684 def reset_cursor_shape(self) -> None: 

685 "Reset cursor shape." 

686 # (Only reset cursor shape, if we ever changed it.) 

687 if self._cursor_shape_changed: 

688 self._cursor_shape_changed = False 

689 

690 # Reset cursor shape. 

691 self.write_raw("\x1b[0 q") 

692 

693 def flush(self) -> None: 

694 """ 

695 Write to output stream and flush. 

696 """ 

697 if not self._buffer: 

698 return 

699 

700 data = "".join(self._buffer) 

701 self._buffer = [] 

702 

703 flush_stdout(self.stdout, data) 

704 

705 def ask_for_cpr(self) -> None: 

706 """ 

707 Asks for a cursor position report (CPR). 

708 """ 

709 self.write_raw("\x1b[6n") 

710 self.flush() 

711 

712 @property 

713 def responds_to_cpr(self) -> bool: 

714 if not self.enable_cpr: 

715 return False 

716 

717 # When the input is a tty, we assume that CPR is supported. 

718 # It's not when the input is piped from Pexpect. 

719 if os.environ.get("PROMPT_TOOLKIT_NO_CPR", "") == "1": 

720 return False 

721 

722 if is_dumb_terminal(self.term): 

723 return False 

724 try: 

725 return self.stdout.isatty() 

726 except ValueError: 

727 return False # ValueError: I/O operation on closed file 

728 

729 def bell(self) -> None: 

730 "Sound bell." 

731 if self.enable_bell: 

732 self.write_raw("\a") 

733 self.flush() 

734 

735 def get_default_color_depth(self) -> ColorDepth: 

736 """ 

737 Return the default color depth for a vt100 terminal, according to the 

738 our term value. 

739 

740 We prefer 256 colors almost always, because this is what most terminals 

741 support these days, and is a good default. 

742 """ 

743 if self.default_color_depth is not None: 

744 return self.default_color_depth 

745 

746 term = self.term 

747 

748 if term is None: 

749 return ColorDepth.DEFAULT 

750 

751 if is_dumb_terminal(term): 

752 return ColorDepth.DEPTH_1_BIT 

753 

754 if term in ("linux", "eterm-color"): 

755 return ColorDepth.DEPTH_4_BIT 

756 

757 return ColorDepth.DEFAULT