]> jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/pip/_vendor/rich/_win32_console.py
init: venv aand flask
[dlqueue.git] / venv / lib / python3.11 / site-packages / pip / _vendor / rich / _win32_console.py
1 """Light wrapper around the Win32 Console API - this module should only be imported on Windows
2
3 The API that this module wraps is documented at https://docs.microsoft.com/en-us/windows/console/console-functions
4 """
5 import ctypes
6 import sys
7 from typing import Any
8
9 windll: Any = None
10 if sys.platform == "win32":
11 windll = ctypes.LibraryLoader(ctypes.WinDLL)
12 else:
13 raise ImportError(f"{__name__} can only be imported on Windows")
14
15 import time
16 from ctypes import Structure, byref, wintypes
17 from typing import IO, NamedTuple, Type, cast
18
19 from pip._vendor.rich.color import ColorSystem
20 from pip._vendor.rich.style import Style
21
22 STDOUT = -11
23 ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
24
25 COORD = wintypes._COORD
26
27
28 class LegacyWindowsError(Exception):
29 pass
30
31
32 class WindowsCoordinates(NamedTuple):
33 """Coordinates in the Windows Console API are (y, x), not (x, y).
34 This class is intended to prevent that confusion.
35 Rows and columns are indexed from 0.
36 This class can be used in place of wintypes._COORD in arguments and argtypes.
37 """
38
39 row: int
40 col: int
41
42 @classmethod
43 def from_param(cls, value: "WindowsCoordinates") -> COORD:
44 """Converts a WindowsCoordinates into a wintypes _COORD structure.
45 This classmethod is internally called by ctypes to perform the conversion.
46
47 Args:
48 value (WindowsCoordinates): The input coordinates to convert.
49
50 Returns:
51 wintypes._COORD: The converted coordinates struct.
52 """
53 return COORD(value.col, value.row)
54
55
56 class CONSOLE_SCREEN_BUFFER_INFO(Structure):
57 _fields_ = [
58 ("dwSize", COORD),
59 ("dwCursorPosition", COORD),
60 ("wAttributes", wintypes.WORD),
61 ("srWindow", wintypes.SMALL_RECT),
62 ("dwMaximumWindowSize", COORD),
63 ]
64
65
66 class CONSOLE_CURSOR_INFO(ctypes.Structure):
67 _fields_ = [("dwSize", wintypes.DWORD), ("bVisible", wintypes.BOOL)]
68
69
70 _GetStdHandle = windll.kernel32.GetStdHandle
71 _GetStdHandle.argtypes = [
72 wintypes.DWORD,
73 ]
74 _GetStdHandle.restype = wintypes.HANDLE
75
76
77 def GetStdHandle(handle: int = STDOUT) -> wintypes.HANDLE:
78 """Retrieves a handle to the specified standard device (standard input, standard output, or standard error).
79
80 Args:
81 handle (int): Integer identifier for the handle. Defaults to -11 (stdout).
82
83 Returns:
84 wintypes.HANDLE: The handle
85 """
86 return cast(wintypes.HANDLE, _GetStdHandle(handle))
87
88
89 _GetConsoleMode = windll.kernel32.GetConsoleMode
90 _GetConsoleMode.argtypes = [wintypes.HANDLE, wintypes.LPDWORD]
91 _GetConsoleMode.restype = wintypes.BOOL
92
93
94 def GetConsoleMode(std_handle: wintypes.HANDLE) -> int:
95 """Retrieves the current input mode of a console's input buffer
96 or the current output mode of a console screen buffer.
97
98 Args:
99 std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer.
100
101 Raises:
102 LegacyWindowsError: If any error occurs while calling the Windows console API.
103
104 Returns:
105 int: Value representing the current console mode as documented at
106 https://docs.microsoft.com/en-us/windows/console/getconsolemode#parameters
107 """
108
109 console_mode = wintypes.DWORD()
110 success = bool(_GetConsoleMode(std_handle, console_mode))
111 if not success:
112 raise LegacyWindowsError("Unable to get legacy Windows Console Mode")
113 return console_mode.value
114
115
116 _FillConsoleOutputCharacterW = windll.kernel32.FillConsoleOutputCharacterW
117 _FillConsoleOutputCharacterW.argtypes = [
118 wintypes.HANDLE,
119 ctypes.c_char,
120 wintypes.DWORD,
121 cast(Type[COORD], WindowsCoordinates),
122 ctypes.POINTER(wintypes.DWORD),
123 ]
124 _FillConsoleOutputCharacterW.restype = wintypes.BOOL
125
126
127 def FillConsoleOutputCharacter(
128 std_handle: wintypes.HANDLE,
129 char: str,
130 length: int,
131 start: WindowsCoordinates,
132 ) -> int:
133 """Writes a character to the console screen buffer a specified number of times, beginning at the specified coordinates.
134
135 Args:
136 std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer.
137 char (str): The character to write. Must be a string of length 1.
138 length (int): The number of times to write the character.
139 start (WindowsCoordinates): The coordinates to start writing at.
140
141 Returns:
142 int: The number of characters written.
143 """
144 character = ctypes.c_char(char.encode())
145 num_characters = wintypes.DWORD(length)
146 num_written = wintypes.DWORD(0)
147 _FillConsoleOutputCharacterW(
148 std_handle,
149 character,
150 num_characters,
151 start,
152 byref(num_written),
153 )
154 return num_written.value
155
156
157 _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute
158 _FillConsoleOutputAttribute.argtypes = [
159 wintypes.HANDLE,
160 wintypes.WORD,
161 wintypes.DWORD,
162 cast(Type[COORD], WindowsCoordinates),
163 ctypes.POINTER(wintypes.DWORD),
164 ]
165 _FillConsoleOutputAttribute.restype = wintypes.BOOL
166
167
168 def FillConsoleOutputAttribute(
169 std_handle: wintypes.HANDLE,
170 attributes: int,
171 length: int,
172 start: WindowsCoordinates,
173 ) -> int:
174 """Sets the character attributes for a specified number of character cells,
175 beginning at the specified coordinates in a screen buffer.
176
177 Args:
178 std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer.
179 attributes (int): Integer value representing the foreground and background colours of the cells.
180 length (int): The number of cells to set the output attribute of.
181 start (WindowsCoordinates): The coordinates of the first cell whose attributes are to be set.
182
183 Returns:
184 int: The number of cells whose attributes were actually set.
185 """
186 num_cells = wintypes.DWORD(length)
187 style_attrs = wintypes.WORD(attributes)
188 num_written = wintypes.DWORD(0)
189 _FillConsoleOutputAttribute(
190 std_handle, style_attrs, num_cells, start, byref(num_written)
191 )
192 return num_written.value
193
194
195 _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute
196 _SetConsoleTextAttribute.argtypes = [
197 wintypes.HANDLE,
198 wintypes.WORD,
199 ]
200 _SetConsoleTextAttribute.restype = wintypes.BOOL
201
202
203 def SetConsoleTextAttribute(
204 std_handle: wintypes.HANDLE, attributes: wintypes.WORD
205 ) -> bool:
206 """Set the colour attributes for all text written after this function is called.
207
208 Args:
209 std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer.
210 attributes (int): Integer value representing the foreground and background colours.
211
212
213 Returns:
214 bool: True if the attribute was set successfully, otherwise False.
215 """
216 return bool(_SetConsoleTextAttribute(std_handle, attributes))
217
218
219 _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo
220 _GetConsoleScreenBufferInfo.argtypes = [
221 wintypes.HANDLE,
222 ctypes.POINTER(CONSOLE_SCREEN_BUFFER_INFO),
223 ]
224 _GetConsoleScreenBufferInfo.restype = wintypes.BOOL
225
226
227 def GetConsoleScreenBufferInfo(
228 std_handle: wintypes.HANDLE,
229 ) -> CONSOLE_SCREEN_BUFFER_INFO:
230 """Retrieves information about the specified console screen buffer.
231
232 Args:
233 std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer.
234
235 Returns:
236 CONSOLE_SCREEN_BUFFER_INFO: A CONSOLE_SCREEN_BUFFER_INFO ctype struct contain information about
237 screen size, cursor position, colour attributes, and more."""
238 console_screen_buffer_info = CONSOLE_SCREEN_BUFFER_INFO()
239 _GetConsoleScreenBufferInfo(std_handle, byref(console_screen_buffer_info))
240 return console_screen_buffer_info
241
242
243 _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition
244 _SetConsoleCursorPosition.argtypes = [
245 wintypes.HANDLE,
246 cast(Type[COORD], WindowsCoordinates),
247 ]
248 _SetConsoleCursorPosition.restype = wintypes.BOOL
249
250
251 def SetConsoleCursorPosition(
252 std_handle: wintypes.HANDLE, coords: WindowsCoordinates
253 ) -> bool:
254 """Set the position of the cursor in the console screen
255
256 Args:
257 std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer.
258 coords (WindowsCoordinates): The coordinates to move the cursor to.
259
260 Returns:
261 bool: True if the function succeeds, otherwise False.
262 """
263 return bool(_SetConsoleCursorPosition(std_handle, coords))
264
265
266 _GetConsoleCursorInfo = windll.kernel32.GetConsoleCursorInfo
267 _GetConsoleCursorInfo.argtypes = [
268 wintypes.HANDLE,
269 ctypes.POINTER(CONSOLE_CURSOR_INFO),
270 ]
271 _GetConsoleCursorInfo.restype = wintypes.BOOL
272
273
274 def GetConsoleCursorInfo(
275 std_handle: wintypes.HANDLE, cursor_info: CONSOLE_CURSOR_INFO
276 ) -> bool:
277 """Get the cursor info - used to get cursor visibility and width
278
279 Args:
280 std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer.
281 cursor_info (CONSOLE_CURSOR_INFO): CONSOLE_CURSOR_INFO ctype struct that receives information
282 about the console's cursor.
283
284 Returns:
285 bool: True if the function succeeds, otherwise False.
286 """
287 return bool(_GetConsoleCursorInfo(std_handle, byref(cursor_info)))
288
289
290 _SetConsoleCursorInfo = windll.kernel32.SetConsoleCursorInfo
291 _SetConsoleCursorInfo.argtypes = [
292 wintypes.HANDLE,
293 ctypes.POINTER(CONSOLE_CURSOR_INFO),
294 ]
295 _SetConsoleCursorInfo.restype = wintypes.BOOL
296
297
298 def SetConsoleCursorInfo(
299 std_handle: wintypes.HANDLE, cursor_info: CONSOLE_CURSOR_INFO
300 ) -> bool:
301 """Set the cursor info - used for adjusting cursor visibility and width
302
303 Args:
304 std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer.
305 cursor_info (CONSOLE_CURSOR_INFO): CONSOLE_CURSOR_INFO ctype struct containing the new cursor info.
306
307 Returns:
308 bool: True if the function succeeds, otherwise False.
309 """
310 return bool(_SetConsoleCursorInfo(std_handle, byref(cursor_info)))
311
312
313 _SetConsoleTitle = windll.kernel32.SetConsoleTitleW
314 _SetConsoleTitle.argtypes = [wintypes.LPCWSTR]
315 _SetConsoleTitle.restype = wintypes.BOOL
316
317
318 def SetConsoleTitle(title: str) -> bool:
319 """Sets the title of the current console window
320
321 Args:
322 title (str): The new title of the console window.
323
324 Returns:
325 bool: True if the function succeeds, otherwise False.
326 """
327 return bool(_SetConsoleTitle(title))
328
329
330 class LegacyWindowsTerm:
331 """This class allows interaction with the legacy Windows Console API. It should only be used in the context
332 of environments where virtual terminal processing is not available. However, if it is used in a Windows environment,
333 the entire API should work.
334
335 Args:
336 file (IO[str]): The file which the Windows Console API HANDLE is retrieved from, defaults to sys.stdout.
337 """
338
339 BRIGHT_BIT = 8
340
341 # Indices are ANSI color numbers, values are the corresponding Windows Console API color numbers
342 ANSI_TO_WINDOWS = [
343 0, # black The Windows colours are defined in wincon.h as follows:
344 4, # red define FOREGROUND_BLUE 0x0001 -- 0000 0001
345 2, # green define FOREGROUND_GREEN 0x0002 -- 0000 0010
346 6, # yellow define FOREGROUND_RED 0x0004 -- 0000 0100
347 1, # blue define FOREGROUND_INTENSITY 0x0008 -- 0000 1000
348 5, # magenta define BACKGROUND_BLUE 0x0010 -- 0001 0000
349 3, # cyan define BACKGROUND_GREEN 0x0020 -- 0010 0000
350 7, # white define BACKGROUND_RED 0x0040 -- 0100 0000
351 8, # bright black (grey) define BACKGROUND_INTENSITY 0x0080 -- 1000 0000
352 12, # bright red
353 10, # bright green
354 14, # bright yellow
355 9, # bright blue
356 13, # bright magenta
357 11, # bright cyan
358 15, # bright white
359 ]
360
361 def __init__(self, file: "IO[str]") -> None:
362 handle = GetStdHandle(STDOUT)
363 self._handle = handle
364 default_text = GetConsoleScreenBufferInfo(handle).wAttributes
365 self._default_text = default_text
366
367 self._default_fore = default_text & 7
368 self._default_back = (default_text >> 4) & 7
369 self._default_attrs = self._default_fore | (self._default_back << 4)
370
371 self._file = file
372 self.write = file.write
373 self.flush = file.flush
374
375 @property
376 def cursor_position(self) -> WindowsCoordinates:
377 """Returns the current position of the cursor (0-based)
378
379 Returns:
380 WindowsCoordinates: The current cursor position.
381 """
382 coord: COORD = GetConsoleScreenBufferInfo(self._handle).dwCursorPosition
383 return WindowsCoordinates(row=cast(int, coord.Y), col=cast(int, coord.X))
384
385 @property
386 def screen_size(self) -> WindowsCoordinates:
387 """Returns the current size of the console screen buffer, in character columns and rows
388
389 Returns:
390 WindowsCoordinates: The width and height of the screen as WindowsCoordinates.
391 """
392 screen_size: COORD = GetConsoleScreenBufferInfo(self._handle).dwSize
393 return WindowsCoordinates(
394 row=cast(int, screen_size.Y), col=cast(int, screen_size.X)
395 )
396
397 def write_text(self, text: str) -> None:
398 """Write text directly to the terminal without any modification of styles
399
400 Args:
401 text (str): The text to write to the console
402 """
403 self.write(text)
404 self.flush()
405
406 def write_styled(self, text: str, style: Style) -> None:
407 """Write styled text to the terminal.
408
409 Args:
410 text (str): The text to write
411 style (Style): The style of the text
412 """
413 color = style.color
414 bgcolor = style.bgcolor
415 if style.reverse:
416 color, bgcolor = bgcolor, color
417
418 if color:
419 fore = color.downgrade(ColorSystem.WINDOWS).number
420 fore = fore if fore is not None else 7 # Default to ANSI 7: White
421 if style.bold:
422 fore = fore | self.BRIGHT_BIT
423 if style.dim:
424 fore = fore & ~self.BRIGHT_BIT
425 fore = self.ANSI_TO_WINDOWS[fore]
426 else:
427 fore = self._default_fore
428
429 if bgcolor:
430 back = bgcolor.downgrade(ColorSystem.WINDOWS).number
431 back = back if back is not None else 0 # Default to ANSI 0: Black
432 back = self.ANSI_TO_WINDOWS[back]
433 else:
434 back = self._default_back
435
436 assert fore is not None
437 assert back is not None
438
439 SetConsoleTextAttribute(
440 self._handle, attributes=ctypes.c_ushort(fore | (back << 4))
441 )
442 self.write_text(text)
443 SetConsoleTextAttribute(self._handle, attributes=self._default_text)
444
445 def move_cursor_to(self, new_position: WindowsCoordinates) -> None:
446 """Set the position of the cursor
447
448 Args:
449 new_position (WindowsCoordinates): The WindowsCoordinates representing the new position of the cursor.
450 """
451 if new_position.col < 0 or new_position.row < 0:
452 return
453 SetConsoleCursorPosition(self._handle, coords=new_position)
454
455 def erase_line(self) -> None:
456 """Erase all content on the line the cursor is currently located at"""
457 screen_size = self.screen_size
458 cursor_position = self.cursor_position
459 cells_to_erase = screen_size.col
460 start_coordinates = WindowsCoordinates(row=cursor_position.row, col=0)
461 FillConsoleOutputCharacter(
462 self._handle, " ", length=cells_to_erase, start=start_coordinates
463 )
464 FillConsoleOutputAttribute(
465 self._handle,
466 self._default_attrs,
467 length=cells_to_erase,
468 start=start_coordinates,
469 )
470
471 def erase_end_of_line(self) -> None:
472 """Erase all content from the cursor position to the end of that line"""
473 cursor_position = self.cursor_position
474 cells_to_erase = self.screen_size.col - cursor_position.col
475 FillConsoleOutputCharacter(
476 self._handle, " ", length=cells_to_erase, start=cursor_position
477 )
478 FillConsoleOutputAttribute(
479 self._handle,
480 self._default_attrs,
481 length=cells_to_erase,
482 start=cursor_position,
483 )
484
485 def erase_start_of_line(self) -> None:
486 """Erase all content from the cursor position to the start of that line"""
487 row, col = self.cursor_position
488 start = WindowsCoordinates(row, 0)
489 FillConsoleOutputCharacter(self._handle, " ", length=col, start=start)
490 FillConsoleOutputAttribute(
491 self._handle, self._default_attrs, length=col, start=start
492 )
493
494 def move_cursor_up(self) -> None:
495 """Move the cursor up a single cell"""
496 cursor_position = self.cursor_position
497 SetConsoleCursorPosition(
498 self._handle,
499 coords=WindowsCoordinates(
500 row=cursor_position.row - 1, col=cursor_position.col
501 ),
502 )
503
504 def move_cursor_down(self) -> None:
505 """Move the cursor down a single cell"""
506 cursor_position = self.cursor_position
507 SetConsoleCursorPosition(
508 self._handle,
509 coords=WindowsCoordinates(
510 row=cursor_position.row + 1,
511 col=cursor_position.col,
512 ),
513 )
514
515 def move_cursor_forward(self) -> None:
516 """Move the cursor forward a single cell. Wrap to the next line if required."""
517 row, col = self.cursor_position
518 if col == self.screen_size.col - 1:
519 row += 1
520 col = 0
521 else:
522 col += 1
523 SetConsoleCursorPosition(
524 self._handle, coords=WindowsCoordinates(row=row, col=col)
525 )
526
527 def move_cursor_to_column(self, column: int) -> None:
528 """Move cursor to the column specified by the zero-based column index, staying on the same row
529
530 Args:
531 column (int): The zero-based column index to move the cursor to.
532 """
533 row, _ = self.cursor_position
534 SetConsoleCursorPosition(self._handle, coords=WindowsCoordinates(row, column))
535
536 def move_cursor_backward(self) -> None:
537 """Move the cursor backward a single cell. Wrap to the previous line if required."""
538 row, col = self.cursor_position
539 if col == 0:
540 row -= 1
541 col = self.screen_size.col - 1
542 else:
543 col -= 1
544 SetConsoleCursorPosition(
545 self._handle, coords=WindowsCoordinates(row=row, col=col)
546 )
547
548 def hide_cursor(self) -> None:
549 """Hide the cursor"""
550 current_cursor_size = self._get_cursor_size()
551 invisible_cursor = CONSOLE_CURSOR_INFO(dwSize=current_cursor_size, bVisible=0)
552 SetConsoleCursorInfo(self._handle, cursor_info=invisible_cursor)
553
554 def show_cursor(self) -> None:
555 """Show the cursor"""
556 current_cursor_size = self._get_cursor_size()
557 visible_cursor = CONSOLE_CURSOR_INFO(dwSize=current_cursor_size, bVisible=1)
558 SetConsoleCursorInfo(self._handle, cursor_info=visible_cursor)
559
560 def set_title(self, title: str) -> None:
561 """Set the title of the terminal window
562
563 Args:
564 title (str): The new title of the console window
565 """
566 assert len(title) < 255, "Console title must be less than 255 characters"
567 SetConsoleTitle(title)
568
569 def _get_cursor_size(self) -> int:
570 """Get the percentage of the character cell that is filled by the cursor"""
571 cursor_info = CONSOLE_CURSOR_INFO()
572 GetConsoleCursorInfo(self._handle, cursor_info=cursor_info)
573 return int(cursor_info.dwSize)
574
575
576 if __name__ == "__main__":
577 handle = GetStdHandle()
578
579 from pip._vendor.rich.console import Console
580
581 console = Console()
582
583 term = LegacyWindowsTerm(sys.stdout)
584 term.set_title("Win32 Console Examples")
585
586 style = Style(color="black", bgcolor="red")
587
588 heading = Style.parse("black on green")
589
590 # Check colour output
591 console.rule("Checking colour output")
592 console.print("[on red]on red!")
593 console.print("[blue]blue!")
594 console.print("[yellow]yellow!")
595 console.print("[bold yellow]bold yellow!")
596 console.print("[bright_yellow]bright_yellow!")
597 console.print("[dim bright_yellow]dim bright_yellow!")
598 console.print("[italic cyan]italic cyan!")
599 console.print("[bold white on blue]bold white on blue!")
600 console.print("[reverse bold white on blue]reverse bold white on blue!")
601 console.print("[bold black on cyan]bold black on cyan!")
602 console.print("[black on green]black on green!")
603 console.print("[blue on green]blue on green!")
604 console.print("[white on black]white on black!")
605 console.print("[black on white]black on white!")
606 console.print("[#1BB152 on #DA812D]#1BB152 on #DA812D!")
607
608 # Check cursor movement
609 console.rule("Checking cursor movement")
610 console.print()
611 term.move_cursor_backward()
612 term.move_cursor_backward()
613 term.write_text("went back and wrapped to prev line")
614 time.sleep(1)
615 term.move_cursor_up()
616 term.write_text("we go up")
617 time.sleep(1)
618 term.move_cursor_down()
619 term.write_text("and down")
620 time.sleep(1)
621 term.move_cursor_up()
622 term.move_cursor_backward()
623 term.move_cursor_backward()
624 term.write_text("we went up and back 2")
625 time.sleep(1)
626 term.move_cursor_down()
627 term.move_cursor_backward()
628 term.move_cursor_backward()
629 term.write_text("we went down and back 2")
630 time.sleep(1)
631
632 # Check erasing of lines
633 term.hide_cursor()
634 console.print()
635 console.rule("Checking line erasing")
636 console.print("\n...Deleting to the start of the line...")
637 term.write_text("The red arrow shows the cursor location, and direction of erase")
638 time.sleep(1)
639 term.move_cursor_to_column(16)
640 term.write_styled("<", Style.parse("black on red"))
641 term.move_cursor_backward()
642 time.sleep(1)
643 term.erase_start_of_line()
644 time.sleep(1)
645
646 console.print("\n\n...And to the end of the line...")
647 term.write_text("The red arrow shows the cursor location, and direction of erase")
648 time.sleep(1)
649
650 term.move_cursor_to_column(16)
651 term.write_styled(">", Style.parse("black on red"))
652 time.sleep(1)
653 term.erase_end_of_line()
654 time.sleep(1)
655
656 console.print("\n\n...Now the whole line will be erased...")
657 term.write_styled("I'm going to disappear!", style=Style.parse("black on cyan"))
658 time.sleep(1)
659 term.erase_line()
660
661 term.show_cursor()
662 print("\n")