Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/prompt_toolkit/output/vt100.py: 33%

321 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +0000

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""" 

9from __future__ import annotations 

10 

11import io 

12import os 

13import sys 

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

15 

16from prompt_toolkit.cursor_shapes import CursorShape 

17from prompt_toolkit.data_structures import Size 

18from prompt_toolkit.output import Output 

19from prompt_toolkit.styles import ANSI_COLOR_NAMES, Attrs 

20from prompt_toolkit.utils import is_dumb_terminal 

21 

22from .color_depth import ColorDepth 

23from .flush_stdout import flush_stdout 

24 

25__all__ = [ 

26 "Vt100_Output", 

27] 

28 

29 

30FG_ANSI_COLORS = { 

31 "ansidefault": 39, 

32 # Low intensity. 

33 "ansiblack": 30, 

34 "ansired": 31, 

35 "ansigreen": 32, 

36 "ansiyellow": 33, 

37 "ansiblue": 34, 

38 "ansimagenta": 35, 

39 "ansicyan": 36, 

40 "ansigray": 37, 

41 # High intensity. 

42 "ansibrightblack": 90, 

43 "ansibrightred": 91, 

44 "ansibrightgreen": 92, 

45 "ansibrightyellow": 93, 

46 "ansibrightblue": 94, 

47 "ansibrightmagenta": 95, 

48 "ansibrightcyan": 96, 

49 "ansiwhite": 97, 

50} 

51 

52BG_ANSI_COLORS = { 

53 "ansidefault": 49, 

54 # Low intensity. 

55 "ansiblack": 40, 

56 "ansired": 41, 

57 "ansigreen": 42, 

58 "ansiyellow": 43, 

59 "ansiblue": 44, 

60 "ansimagenta": 45, 

61 "ansicyan": 46, 

62 "ansigray": 47, 

63 # High intensity. 

64 "ansibrightblack": 100, 

65 "ansibrightred": 101, 

66 "ansibrightgreen": 102, 

67 "ansibrightyellow": 103, 

68 "ansibrightblue": 104, 

69 "ansibrightmagenta": 105, 

70 "ansibrightcyan": 106, 

71 "ansiwhite": 107, 

72} 

73 

74 

75ANSI_COLORS_TO_RGB = { 

76 "ansidefault": ( 

77 0x00, 

78 0x00, 

79 0x00, 

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

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

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

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

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

85 # Low intensity. 

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

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

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

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

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

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

92 # High intensity. 

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

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

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

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

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

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

99} 

100 

101 

102assert set(FG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) 

103assert set(BG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) 

104assert set(ANSI_COLORS_TO_RGB) == set(ANSI_COLOR_NAMES) 

105 

106 

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

108 """ 

109 Find closest ANSI color. Return it by name. 

110 

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

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

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

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

115 """ 

116 exclude = list(exclude) 

117 

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

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

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

121 

122 if saturation > 30: 

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

124 

125 # Take the closest color. 

126 # (Thanks to Pygments for this part.) 

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

128 match = "ansidefault" 

129 

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

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

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

133 

134 if d < distance: 

135 match = name 

136 distance = d 

137 

138 return match 

139 

140 

141_ColorCodeAndName = Tuple[int, str] 

142 

143 

144class _16ColorCache: 

145 """ 

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

147 

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

149 """ 

150 

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

152 self.bg = bg 

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

154 

155 def get_code( 

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

157 ) -> _ColorCodeAndName: 

158 """ 

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

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

161 """ 

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

163 cache = self._cache 

164 

165 if key not in cache: 

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

167 

168 return cache[key] 

169 

170 def _get( 

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

172 ) -> _ColorCodeAndName: 

173 r, g, b = value 

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

175 

176 # Turn color name into code. 

177 if self.bg: 

178 code = BG_ANSI_COLORS[match] 

179 else: 

180 code = FG_ANSI_COLORS[match] 

181 

182 return code, match 

183 

184 

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

186 """ 

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

188 """ 

189 

190 def __init__(self) -> None: 

191 # Build color table. 

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

193 

194 # colors 0..15: 16 basic colors 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

211 

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

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

214 

215 for i in range(217): 

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

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

218 b = valuerange[i % 6] 

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

220 

221 # colors 233..253: grayscale 

222 for i in range(1, 22): 

223 v = 8 + i * 10 

224 colors.append((v, v, v)) 

225 

226 self.colors = colors 

227 

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

229 r, g, b = value 

230 

231 # Find closest color. 

232 # (Thanks to Pygments for this!) 

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

234 match = 0 

235 

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

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

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

239 # the color scheme of the terminal. 

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

241 

242 if d < distance: 

243 match = i 

244 distance = d 

245 

246 # Turn color name into code. 

247 self[value] = match 

248 return match 

249 

250 

251_16_fg_colors = _16ColorCache(bg=False) 

252_16_bg_colors = _16ColorCache(bg=True) 

253_256_colors = _256ColorCache() 

254 

255 

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

257 """ 

258 Cache for VT100 escape codes. It maps 

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

260 escape sequences. 

261 

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

263 """ 

264 

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

266 self.color_depth = color_depth 

267 

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

269 ( 

270 fgcolor, 

271 bgcolor, 

272 bold, 

273 underline, 

274 strike, 

275 italic, 

276 blink, 

277 reverse, 

278 hidden, 

279 ) = attrs 

280 parts: list[str] = [] 

281 

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

283 

284 if bold: 

285 parts.append("1") 

286 if italic: 

287 parts.append("3") 

288 if blink: 

289 parts.append("5") 

290 if underline: 

291 parts.append("4") 

292 if reverse: 

293 parts.append("7") 

294 if hidden: 

295 parts.append("8") 

296 if strike: 

297 parts.append("9") 

298 

299 if parts: 

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

301 else: 

302 result = "\x1b[0m" 

303 

304 self[attrs] = result 

305 return result 

306 

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

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

309 try: 

310 rgb = int(color, 16) 

311 except ValueError: 

312 raise 

313 else: 

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

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

316 b = rgb & 0xFF 

317 return r, g, b 

318 

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

320 """ 

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

322 """ 

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

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

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

326 fg_ansi = "" 

327 

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

329 nonlocal fg_ansi 

330 

331 table = BG_ANSI_COLORS if bg else FG_ANSI_COLORS 

332 

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

334 return [] 

335 

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

337 elif color in table: 

338 return [table[color]] 

339 

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

341 else: 

342 try: 

343 rgb = self._color_name_to_rgb(color) 

344 except ValueError: 

345 return [] 

346 

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

348 if self.color_depth == ColorDepth.DEPTH_4_BIT: 

349 if bg: # Background. 

350 if fg_color != bg_color: 

351 exclude = [fg_ansi] 

352 else: 

353 exclude = [] 

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

355 return [code] 

356 else: # Foreground. 

357 code, name = _16_fg_colors.get_code(rgb) 

358 fg_ansi = name 

359 return [code] 

360 

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

362 elif self.color_depth == ColorDepth.DEPTH_24_BIT: 

363 r, g, b = rgb 

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

365 

366 # 256 RGB colors. 

367 else: 

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

369 

370 result: list[int] = [] 

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

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

373 

374 return map(str, result) 

375 

376 

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

378 """ 

379 Get the size of this pseudo terminal. 

380 

381 :param fileno: stdout.fileno() 

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

383 """ 

384 size = os.get_terminal_size(fileno) 

385 return size.lines, size.columns 

386 

387 

388class Vt100_Output(Output): 

389 """ 

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

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

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

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

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

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

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

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

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

399 requests. 

400 """ 

401 

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

403 # file descriptor. 

404 _fds_not_a_terminal: set[int] = set() 

405 

406 def __init__( 

407 self, 

408 stdout: TextIO, 

409 get_size: Callable[[], Size], 

410 term: str | None = None, 

411 default_color_depth: ColorDepth | None = None, 

412 enable_bell: bool = True, 

413 enable_cpr: bool = True, 

414 ) -> None: 

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

416 

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

418 self.stdout: TextIO = stdout 

419 self.default_color_depth = default_color_depth 

420 self._get_size = get_size 

421 self.term = term 

422 self.enable_bell = enable_bell 

423 self.enable_cpr = enable_cpr 

424 

425 # Cache for escape codes. 

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

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

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

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

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

431 } 

432 

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

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

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

436 self._cursor_shape_changed = False 

437 

438 @classmethod 

439 def from_pty( 

440 cls, 

441 stdout: TextIO, 

442 term: str | None = None, 

443 default_color_depth: ColorDepth | None = None, 

444 enable_bell: bool = True, 

445 ) -> Vt100_Output: 

446 """ 

447 Create an Output class from a pseudo terminal. 

448 (This will take the dimensions by reading the pseudo 

449 terminal attributes.) 

450 """ 

451 fd: int | None 

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

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

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

455 try: 

456 fd = stdout.fileno() 

457 except io.UnsupportedOperation: 

458 fd = None 

459 

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

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

462 sys.stderr.write(msg % fd) 

463 sys.stderr.flush() 

464 if fd is not None: 

465 cls._fds_not_a_terminal.add(fd) 

466 

467 def get_size() -> Size: 

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

469 # reasonable default. See 

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

471 rows, columns = (None, None) 

472 

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

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

475 # `get_size`. See: 

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

477 try: 

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

479 except OSError: 

480 pass 

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

482 

483 return cls( 

484 stdout, 

485 get_size, 

486 term=term, 

487 default_color_depth=default_color_depth, 

488 enable_bell=enable_bell, 

489 ) 

490 

491 def get_size(self) -> Size: 

492 return self._get_size() 

493 

494 def fileno(self) -> int: 

495 "Return file descriptor." 

496 return self.stdout.fileno() 

497 

498 def encoding(self) -> str: 

499 "Return encoding used for stdout." 

500 return self.stdout.encoding 

501 

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

503 """ 

504 Write raw data to output. 

505 """ 

506 self._buffer.append(data) 

507 

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

509 """ 

510 Write text to output. 

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

512 """ 

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

514 

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

516 """ 

517 Set terminal title. 

518 """ 

519 if self.term not in ( 

520 "linux", 

521 "eterm-color", 

522 ): # Not supported by the Linux console. 

523 self.write_raw( 

524 "\x1b]2;%s\x07" % title.replace("\x1b", "").replace("\x07", "") 

525 ) 

526 

527 def clear_title(self) -> None: 

528 self.set_title("") 

529 

530 def erase_screen(self) -> None: 

531 """ 

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

533 home. 

534 """ 

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

536 

537 def enter_alternate_screen(self) -> None: 

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

539 

540 def quit_alternate_screen(self) -> None: 

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

542 

543 def enable_mouse_support(self) -> None: 

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

545 

546 # Enable mouse-drag support. 

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

548 

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

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

551 

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

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

554 

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

556 # extensions. 

557 

558 def disable_mouse_support(self) -> None: 

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

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

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

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

563 

564 def erase_end_of_line(self) -> None: 

565 """ 

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

567 """ 

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

569 

570 def erase_down(self) -> None: 

571 """ 

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

573 screen. 

574 """ 

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

576 

577 def reset_attributes(self) -> None: 

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

579 

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

581 """ 

582 Create new style and output. 

583 

584 :param attrs: `Attrs` instance. 

585 """ 

586 # Get current depth. 

587 escape_code_cache = self._escape_code_caches[color_depth] 

588 

589 # Write escape character. 

590 self.write_raw(escape_code_cache[attrs]) 

591 

592 def disable_autowrap(self) -> None: 

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

594 

595 def enable_autowrap(self) -> None: 

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

597 

598 def enable_bracketed_paste(self) -> None: 

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

600 

601 def disable_bracketed_paste(self) -> None: 

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

603 

604 def reset_cursor_key_mode(self) -> None: 

605 """ 

606 For vt100 only. 

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

608 """ 

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

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

611 

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

613 """ 

614 Move cursor position. 

615 """ 

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

617 

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

619 if amount == 0: 

620 pass 

621 elif amount == 1: 

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

623 else: 

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

625 

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

627 if amount == 0: 

628 pass 

629 elif amount == 1: 

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

631 # scroll. 

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

633 else: 

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

635 

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

637 if amount == 0: 

638 pass 

639 elif amount == 1: 

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

641 else: 

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

643 

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

645 if amount == 0: 

646 pass 

647 elif amount == 1: 

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

649 else: 

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

651 

652 def hide_cursor(self) -> None: 

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

654 

655 def show_cursor(self) -> None: 

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

657 

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

659 if cursor_shape == CursorShape._NEVER_CHANGE: 

660 return 

661 

662 self._cursor_shape_changed = True 

663 self.write_raw( 

664 { 

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

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

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

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

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

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

671 }.get(cursor_shape, "") 

672 ) 

673 

674 def reset_cursor_shape(self) -> None: 

675 "Reset cursor shape." 

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

677 if self._cursor_shape_changed: 

678 self._cursor_shape_changed = False 

679 

680 # Reset cursor shape. 

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

682 

683 def flush(self) -> None: 

684 """ 

685 Write to output stream and flush. 

686 """ 

687 if not self._buffer: 

688 return 

689 

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

691 self._buffer = [] 

692 

693 flush_stdout(self.stdout, data) 

694 

695 def ask_for_cpr(self) -> None: 

696 """ 

697 Asks for a cursor position report (CPR). 

698 """ 

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

700 self.flush() 

701 

702 @property 

703 def responds_to_cpr(self) -> bool: 

704 if not self.enable_cpr: 

705 return False 

706 

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

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

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

710 return False 

711 

712 if is_dumb_terminal(self.term): 

713 return False 

714 try: 

715 return self.stdout.isatty() 

716 except ValueError: 

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

718 

719 def bell(self) -> None: 

720 "Sound bell." 

721 if self.enable_bell: 

722 self.write_raw("\a") 

723 self.flush() 

724 

725 def get_default_color_depth(self) -> ColorDepth: 

726 """ 

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

728 our term value. 

729 

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

731 support these days, and is a good default. 

732 """ 

733 if self.default_color_depth is not None: 

734 return self.default_color_depth 

735 

736 term = self.term 

737 

738 if term is None: 

739 return ColorDepth.DEFAULT 

740 

741 if is_dumb_terminal(term): 

742 return ColorDepth.DEPTH_1_BIT 

743 

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

745 return ColorDepth.DEPTH_4_BIT 

746 

747 return ColorDepth.DEFAULT