Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1a11ea3
Conversion of Segments to legacy Windows API calls
darrenburns Mar 1, 2022
fe57132
Add None windll definition for mypy
darrenburns Mar 1, 2022
ffc5813
Attempting to appease mypy on non-Windows platforms
darrenburns Mar 1, 2022
fb227b8
Use type of Any for windll
darrenburns Mar 1, 2022
e384106
On legacy Windows, we sometimes still need ANSI output...
darrenburns Mar 1, 2022
78fc8d9
Only calling Windows legacy console API if Console.file is stdout
darrenburns Mar 1, 2022
1d38b45
Handling utf-8 error in check_buffer consistently across Windows & Unix
darrenburns Mar 1, 2022
6779e51
Don't default to outputting ANSI just because Console.record=True
darrenburns Mar 1, 2022
bb9ab3c
Merge branch 'master' of https://github.com/Textualize/rich into ansi…
darrenburns Mar 1, 2022
501dcbd
Add tests for LegacyWindowsTerm
darrenburns Mar 1, 2022
97d05f5
Fixing test module on non-Windows platforms
darrenburns Mar 1, 2022
7d08e0a
Fixing test module on non-Windows platforms
darrenburns Mar 1, 2022
151276b
Run legacy Windows tests on Windows only
darrenburns Mar 2, 2022
862416a
Fix typing issues
darrenburns Mar 2, 2022
7525377
Use Python 3.6 & 3.7 compatible means of acceessing mock call args/kw…
darrenburns Mar 2, 2022
80912a5
Update CHANGELOG.md
darrenburns Mar 2, 2022
28786c7
Merge branch 'ansi-to-win32' of https://github.com/Textualize/rich in…
darrenburns Mar 2, 2022
ceef724
Use Windows Console API to write text
darrenburns Mar 3, 2022
1122501
Use WriteConsoleW instead of file.write(...) in LegacyWindowsTerm
darrenburns Mar 3, 2022
d87498b
Use bitwise operators in LegacyWindowsTerm, fix formatting
darrenburns Mar 3, 2022
5a051f9
Make GetConsoleMode Windows Console wrapper more Pythonic
darrenburns Mar 3, 2022
8f738c7
Support reverse, bold (bright), and dim
darrenburns Mar 3, 2022
ca3e966
Handle legacy windows error
darrenburns Mar 3, 2022
81c4dc4
Add docstrings to Windows console wrapper functions
darrenburns Mar 3, 2022
9b76da2
Merge pull request #2019 from Textualize/ansi-to-win32_pull-request-f…
darrenburns Mar 7, 2022
8fe170a
Merge branch 'master' into ansi-to-win32
willmcgugan Mar 9, 2022
91e0146
check win32
willmcgugan Mar 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix typing issues
  • Loading branch information
darrenburns committed Mar 2, 2022
commit 862416acf4fa1dadd0e3fc5eab9212890436c4df
22 changes: 11 additions & 11 deletions rich/_win32_console.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"""Light wrapper around the win32 Console API - this module should only be imported on Windows"""
import ctypes
import sys
from typing import IO, NamedTuple, Type, cast
from typing import IO, Any, NamedTuple, Type, cast

windll: Any = None
if sys.platform == "win32":
windll = ctypes.LibraryLoader(ctypes.WinDLL)
else:
Expand All @@ -18,7 +19,6 @@
STDOUT = -11
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4

kernel32 = windll.kernel32
COORD = wintypes._COORD


Expand Down Expand Up @@ -51,7 +51,7 @@ class CONSOLE_CURSOR_INFO(ctypes.Structure):
_fields_ = [("dwSize", wintypes.DWORD), ("bVisible", wintypes.BOOL)]


_GetStdHandle = kernel32.GetStdHandle
_GetStdHandle = windll.kernel32.GetStdHandle
_GetStdHandle.argtypes = [
wintypes.DWORD,
]
Expand All @@ -62,7 +62,7 @@ def GetStdHandle(handle: int = STDOUT) -> wintypes.HANDLE:
return cast(wintypes.HANDLE, _GetStdHandle(handle))


_GetConsoleMode = kernel32.GetConsoleMode
_GetConsoleMode = windll.kernel32.GetConsoleMode
_GetConsoleMode.argtypes = [wintypes.HANDLE, wintypes.LPDWORD]
_GetConsoleMode.restype = wintypes.BOOL

Expand All @@ -71,7 +71,7 @@ def GetConsoleMode(std_handle: wintypes.HANDLE, console_mode: wintypes.DWORD) ->
return bool(_GetConsoleMode(std_handle, console_mode))


_FillConsoleOutputCharacterW = kernel32.FillConsoleOutputCharacterW
_FillConsoleOutputCharacterW = windll.kernel32.FillConsoleOutputCharacterW
_FillConsoleOutputCharacterW.argtypes = [
wintypes.HANDLE,
ctypes.c_char,
Expand Down Expand Up @@ -103,7 +103,7 @@ def FillConsoleOutputCharacter(
return num_written.value


_FillConsoleOutputAttribute = kernel32.FillConsoleOutputAttribute
_FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute
_FillConsoleOutputAttribute.argtypes = [
wintypes.HANDLE,
wintypes.WORD,
Expand All @@ -129,7 +129,7 @@ def FillConsoleOutputAttribute(
return num_written.value


_SetConsoleTextAttribute = kernel32.SetConsoleTextAttribute
_SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute
_SetConsoleTextAttribute.argtypes = [
wintypes.HANDLE,
wintypes.WORD,
Expand All @@ -143,7 +143,7 @@ def SetConsoleTextAttribute(
return bool(_SetConsoleTextAttribute(std_handle, attributes))


_GetConsoleScreenBufferInfo = kernel32.GetConsoleScreenBufferInfo
_GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo
_GetConsoleScreenBufferInfo.argtypes = [
wintypes.HANDLE,
ctypes.POINTER(CONSOLE_SCREEN_BUFFER_INFO),
Expand All @@ -159,7 +159,7 @@ def GetConsoleScreenBufferInfo(
return console_screen_buffer_info


_SetConsoleCursorPosition = kernel32.SetConsoleCursorPosition
_SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition
_SetConsoleCursorPosition.argtypes = [
wintypes.HANDLE,
cast(Type[COORD], WindowsCoordinates),
Expand All @@ -173,7 +173,7 @@ def SetConsoleCursorPosition(
return bool(_SetConsoleCursorPosition(std_handle, coords))


_SetConsoleCursorInfo = kernel32.SetConsoleCursorInfo
_SetConsoleCursorInfo = windll.kernel32.SetConsoleCursorInfo
_SetConsoleCursorInfo.argtypes = [
wintypes.HANDLE,
ctypes.POINTER(CONSOLE_CURSOR_INFO),
Expand All @@ -187,7 +187,7 @@ def SetConsoleCursorInfo(
return bool(_SetConsoleCursorInfo(std_handle, byref(cursor_info)))


_SetConsoleTitle = kernel32.SetConsoleTitleW
_SetConsoleTitle = windll.kernel32.SetConsoleTitleW
_SetConsoleTitle.argtypes = [wintypes.LPCWSTR]
_SetConsoleTitle.restype = wintypes.BOOL

Expand Down
68 changes: 27 additions & 41 deletions tests/test_win32_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@

if sys.platform == "win32":

from rich._win32_console import COORD, LegacyWindowsTerm, WindowsCoordinates
from rich import _win32_console
from rich._win32_console import COORD, LegacyWindowsTerm, WindowsCoordinates

CURSOR_X = 1
CURSOR_Y = 2
Expand All @@ -20,45 +20,40 @@
SCREEN_HEIGHT = 30
DEFAULT_STYLE_ATTRIBUTE = 16


@dataclasses.dataclass
class StubScreenBufferInfo:
dwCursorPosition: COORD = COORD(CURSOR_X, CURSOR_Y)
dwSize: COORD = COORD(SCREEN_WIDTH, SCREEN_HEIGHT)
wAttributes: int = DEFAULT_STYLE_ATTRIBUTE


pytestmark = pytest.mark.skipif(sys.platform != "win32", reason="windows only")


def test_windows_coordinates_to_ctype():
coord = WindowsCoordinates.from_param(WindowsCoordinates(row=1, col=2))
assert coord.X == 2
assert coord.Y == 1


@pytest.fixture
def win32_handle():
handle = mock.sentinel
with mock.patch.object(_win32_console, "GetStdHandle", return_value=handle):
yield handle


@patch.object(
_win32_console, "GetConsoleScreenBufferInfo", return_value=StubScreenBufferInfo
)
def test_cursor_position(_):
term = LegacyWindowsTerm()
assert term.cursor_position == WindowsCoordinates(row=CURSOR_Y, col=CURSOR_X)


@patch.object(
_win32_console, "GetConsoleScreenBufferInfo", return_value=StubScreenBufferInfo
)
def test_screen_size(_):
term = LegacyWindowsTerm()
assert term.screen_size == WindowsCoordinates(row=SCREEN_HEIGHT, col=SCREEN_WIDTH)

assert term.screen_size == WindowsCoordinates(
row=SCREEN_HEIGHT, col=SCREEN_WIDTH
)

@patch.object(
_win32_console, "GetConsoleScreenBufferInfo", return_value=StubScreenBufferInfo
Expand All @@ -72,7 +67,6 @@ def test_write_text(_):

assert f.getvalue() == text


@patch.object(_win32_console, "SetConsoleTextAttribute")
@patch.object(
_win32_console, "GetConsoleScreenBufferInfo", return_value=StubScreenBufferInfo
Expand All @@ -93,14 +87,13 @@ def test_write_styled(_, SetConsoleTextAttribute, win32_handle):
assert call_args[0].kwargs["attributes"].value == 64
assert call_args[1] == call(win32_handle, attributes=DEFAULT_STYLE_ATTRIBUTE)


@patch.object(_win32_console, "FillConsoleOutputCharacter", return_value=None)
@patch.object(_win32_console, "FillConsoleOutputAttribute", return_value=None)
@patch.object(
_win32_console, "GetConsoleScreenBufferInfo", return_value=StubScreenBufferInfo
)
def test_erase_line(
_, FillConsoleOutputAttribute, FillConsoleOutputCharacter, win32_handle
_, FillConsoleOutputAttribute, FillConsoleOutputCharacter, win32_handle
):
term = LegacyWindowsTerm()
term.erase_line()
Expand All @@ -112,14 +105,13 @@ def test_erase_line(
win32_handle, DEFAULT_STYLE_ATTRIBUTE, length=SCREEN_WIDTH, start=start
)


@patch.object(_win32_console, "FillConsoleOutputCharacter", return_value=None)
@patch.object(_win32_console, "FillConsoleOutputAttribute", return_value=None)
@patch.object(
_win32_console, "GetConsoleScreenBufferInfo", return_value=StubScreenBufferInfo
)
def test_erase_end_of_line(
_, FillConsoleOutputAttribute, FillConsoleOutputCharacter, win32_handle
_, FillConsoleOutputAttribute, FillConsoleOutputCharacter, win32_handle
):
term = LegacyWindowsTerm()
term.erase_end_of_line()
Expand All @@ -134,14 +126,13 @@ def test_erase_end_of_line(
start=CURSOR_POSITION,
)


@patch.object(_win32_console, "FillConsoleOutputCharacter", return_value=None)
@patch.object(_win32_console, "FillConsoleOutputAttribute", return_value=None)
@patch.object(
_win32_console, "GetConsoleScreenBufferInfo", return_value=StubScreenBufferInfo
)
def test_erase_start_of_line(
_, FillConsoleOutputAttribute, FillConsoleOutputCharacter, win32_handle
_, FillConsoleOutputAttribute, FillConsoleOutputCharacter, win32_handle
):
term = LegacyWindowsTerm()
term.erase_start_of_line()
Expand All @@ -155,7 +146,6 @@ def test_erase_start_of_line(
win32_handle, DEFAULT_STYLE_ATTRIBUTE, length=CURSOR_X, start=start
)


@patch.object(_win32_console, "SetConsoleCursorPosition", return_value=None)
@patch.object(
_win32_console, "GetConsoleScreenBufferInfo", return_value=StubScreenBufferInfo
Expand All @@ -168,33 +158,34 @@ def test_move_cursor_to(_, SetConsoleCursorPosition, win32_handle):

SetConsoleCursorPosition.assert_called_once_with(win32_handle, coords=coords)


@patch.object(_win32_console, "SetConsoleCursorPosition", return_value=None)
@patch.object(
_win32_console, "GetConsoleScreenBufferInfo", return_value=StubScreenBufferInfo
)
def test_move_cursor_to_out_of_bounds_row(_, SetConsoleCursorPosition, win32_handle):
def test_move_cursor_to_out_of_bounds_row(
_, SetConsoleCursorPosition, win32_handle
):
coords = WindowsCoordinates(row=-1, col=4)
term = LegacyWindowsTerm()

term.move_cursor_to(coords)

assert not SetConsoleCursorPosition.called


@patch.object(_win32_console, "SetConsoleCursorPosition", return_value=None)
@patch.object(
_win32_console, "GetConsoleScreenBufferInfo", return_value=StubScreenBufferInfo
)
def test_move_cursor_to_out_of_bounds_col(_, SetConsoleCursorPosition, win32_handle):
def test_move_cursor_to_out_of_bounds_col(
_, SetConsoleCursorPosition, win32_handle
):
coords = WindowsCoordinates(row=10, col=-4)
term = LegacyWindowsTerm()

term.move_cursor_to(coords)

assert not SetConsoleCursorPosition.called


@patch.object(_win32_console, "SetConsoleCursorPosition", return_value=None)
@patch.object(
_win32_console, "GetConsoleScreenBufferInfo", return_value=StubScreenBufferInfo
Expand All @@ -208,7 +199,6 @@ def test_move_cursor_up(_, SetConsoleCursorPosition, win32_handle):
win32_handle, coords=WindowsCoordinates(row=CURSOR_Y - 1, col=CURSOR_X)
)


@patch.object(_win32_console, "SetConsoleCursorPosition", return_value=None)
@patch.object(
_win32_console, "GetConsoleScreenBufferInfo", return_value=StubScreenBufferInfo
Expand All @@ -222,7 +212,6 @@ def test_move_cursor_down(_, SetConsoleCursorPosition, win32_handle):
win32_handle, coords=WindowsCoordinates(row=CURSOR_Y + 1, col=CURSOR_X)
)


@patch.object(_win32_console, "SetConsoleCursorPosition", return_value=None)
@patch.object(
_win32_console, "GetConsoleScreenBufferInfo", return_value=StubScreenBufferInfo
Expand All @@ -236,16 +225,15 @@ def test_move_cursor_forward(_, SetConsoleCursorPosition, win32_handle):
win32_handle, coords=WindowsCoordinates(row=CURSOR_Y, col=CURSOR_X + 1)
)


@patch.object(_win32_console, "SetConsoleCursorPosition", return_value=None)
def test_move_cursor_forward_newline_wrap(SetConsoleCursorPosition, win32_handle):
cursor_at_end_of_line = StubScreenBufferInfo(
dwCursorPosition=COORD(SCREEN_WIDTH - 1, CURSOR_Y)
)
with patch.object(
_win32_console,
"GetConsoleScreenBufferInfo",
return_value=cursor_at_end_of_line,
_win32_console,
"GetConsoleScreenBufferInfo",
return_value=cursor_at_end_of_line,
):
term = LegacyWindowsTerm()
term.move_cursor_forward()
Expand All @@ -254,7 +242,6 @@ def test_move_cursor_forward_newline_wrap(SetConsoleCursorPosition, win32_handle
win32_handle, coords=WindowsCoordinates(row=CURSOR_Y + 1, col=0)
)


@patch.object(_win32_console, "SetConsoleCursorPosition", return_value=None)
@patch.object(
_win32_console, "GetConsoleScreenBufferInfo", return_value=StubScreenBufferInfo
Expand All @@ -266,7 +253,6 @@ def test_move_cursor_to_column(_, SetConsoleCursorPosition, win32_handle):
win32_handle, coords=WindowsCoordinates(CURSOR_Y, 5)
)


@patch.object(_win32_console, "SetConsoleCursorPosition", return_value=None)
@patch.object(
_win32_console, "GetConsoleScreenBufferInfo", return_value=StubScreenBufferInfo
Expand All @@ -278,22 +264,25 @@ def test_move_cursor_backward(_, SetConsoleCursorPosition, win32_handle):
win32_handle, coords=WindowsCoordinates(row=CURSOR_Y, col=CURSOR_X - 1)
)


@patch.object(_win32_console, "SetConsoleCursorPosition", return_value=None)
def test_move_cursor_backward_prev_line_wrap(SetConsoleCursorPosition, win32_handle):
cursor_at_start_of_line = StubScreenBufferInfo(dwCursorPosition=COORD(0, CURSOR_Y))
def test_move_cursor_backward_prev_line_wrap(
SetConsoleCursorPosition, win32_handle
):
cursor_at_start_of_line = StubScreenBufferInfo(
dwCursorPosition=COORD(0, CURSOR_Y)
)
with patch.object(
_win32_console,
"GetConsoleScreenBufferInfo",
return_value=cursor_at_start_of_line,
_win32_console,
"GetConsoleScreenBufferInfo",
return_value=cursor_at_start_of_line,
):
term = LegacyWindowsTerm()
term.move_cursor_backward()
SetConsoleCursorPosition.assert_called_once_with(
win32_handle, coords=WindowsCoordinates(row=CURSOR_Y - 1, col=SCREEN_WIDTH - 1)
win32_handle,
coords=WindowsCoordinates(row=CURSOR_Y - 1, col=SCREEN_WIDTH - 1),
)


@patch.object(_win32_console, "SetConsoleCursorInfo", return_value=None)
@patch.object(
_win32_console, "GetConsoleScreenBufferInfo", return_value=StubScreenBufferInfo
Expand All @@ -308,7 +297,6 @@ def test_hide_cursor(_, SetConsoleCursorInfo, win32_handle):
assert call_args[0].kwargs["cursor_info"].bVisible == 0
assert call_args[0].kwargs["cursor_info"].dwSize == 100


@patch.object(_win32_console, "SetConsoleCursorInfo", return_value=None)
@patch.object(
_win32_console, "GetConsoleScreenBufferInfo", return_value=StubScreenBufferInfo
Expand All @@ -323,15 +311,13 @@ def test_show_cursor(_, SetConsoleCursorInfo, win32_handle):
assert call_args[0].kwargs["cursor_info"].bVisible == 1
assert call_args[0].kwargs["cursor_info"].dwSize == 100


@patch.object(_win32_console, "SetConsoleTitle", return_value=None)
def test_set_title(SetConsoleTitle):
term = LegacyWindowsTerm()
term.set_title("title")

SetConsoleTitle.assert_called_once_with("title")


@patch.object(_win32_console, "SetConsoleTitle", return_value=None)
def test_set_title_too_long(_):
term = LegacyWindowsTerm()
Expand Down