Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Console.show_cursor() (and displaying Rich progress bars) clobbers cursor style on Windows #2333

Closed
CAM-Gerlach opened this issue Jun 9, 2022 · 2 comments · Fixed by #2339
Assignees

Comments

@CAM-Gerlach
Copy link

CAM-Gerlach commented Jun 9, 2022

Describe the bug

For some context, I originally discovered this with the Rich progress bars in Twine, that whenever one would display, the cursor style would be clobbered to a block cursor, and reported as pypa/twine#902 . However, I subsequently reproduced it in pip as and was directed to report it to Rich, and indeed, I can reproduce it with a minimal Rich snippit.

On Windows (tested both 8.1 and 10 on multiple machines) running rich.console.Console.show_cursor() clobbers the current cursor style (underbar in the stock Windows terminals, bar in ConEmu) and forces the cursor to display as a block, even if the cursor is already visible, with no way I've found to get it back other than completely closing and restarting the terminal. This also happens whenever I run pip and Twine and Rich progress bars are displayed, unless I specifically disable them through their respective CLIs, which I have to always remember to do, or I have to restart my terminal tab to get my normal cursor back, potentially losing tens of thousands of lines of scrollback and command history.

I've reproduced the same behavior in all environments on my machine I was able to test, as well as the other clean-installed Windows machines I tested (with no obvious common configuration):

  • cmd.exe shell running in the default Command Prompt terminal
  • Powershell running in the default Powershell terminal
  • cmd.exe running in ConEmu
  • Git Bash running in ConEmu

(In Git Bash running in its bundled MinTTY, I could not confirm if the behavior can be reproduced since Rich treats it as a not a terminal and the function terminates without doing anything, given is_terminal is False there and show_cursor(False) does nothing.)

Furthermore, I've reproduced the above running the latest version of Rich, 12.4.4, in fresh clean environments with only Rich, pip, Twine and their deps installed, both from PyPI with pip, and from conda-forge with conda.

I've tried numerous methods to restore it once this happens, including modifying my terminal emulator settings, various ANSI escape sequences, the reset command in my shell, starting a new bash session within an existing one and sshing into another machine, including a Linux box, but nothing works save closing and restarting the terminal.

However, if I ssh into a Linux host (prior to triggering the bug) from either ConEmu + Git Bash or MinTTY + Git Bash and run show_cursor() there, the cursor both hides and shows correctly, retaining whatever shape I configured in my terminal emulator (and I tested with lines, blocks and underbars). (To note, this works even from MinTTY running Git Bash, despite it not being considered a terminal when running bash on Windows.) The cursor hiding/showing persists (correctly, I assume) even when I exist the remote session, but if I do trigger the bug on Windows, so does the block cursor within and without the ssh session (and show_cursor() does not restore the previous one, since the block cursor is set as the current cursor). Therefore, it would seem there is something specific to Windows that is triggering this issue, given I'm running the same shell and same terminal emulator and everything works fine.

Minimal reproducer:

python -c "import rich.console; rich.console.Console().show_cursor()"
# Cursor now a block

Platform

What platform (Win/Linux/Mac) are you running on? What terminal software are you using?

Reproduced issue

All running Python 3.9.13 x64

  • {1*) Windows 8.1 Pro x64 + ConEmu 210912 stable + Git Bash 4.4.23(1)-release (x86_64-pc-msys)
  • Windows 8.1 Pro x64 + ConEmu 210912 stable + cmd.exe
  • Windows 8.1 Pro x64 + Command Prompt + cmd.exe
  • Windows 8.1 Pro x64 + Windows Powershell + powershell
  • Windows 10 Enterprise x64 + Command Prompt + cmd.exe
Windows + ConEmu + Git Bash platform details

python -m rich and python -m rich.diagnose display colors and other characters correctly, as far as I can tell.

$ python -m pip list | grep rich
rich               12.4.4

$ python -m rich.diagnose
┌───────────────────────── <class 'rich.console.Console'> ─────────────────────────┐
│ A high level console interface.                                                  │
│                                                                                  │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ <console width=117 ColorSystem.WINDOWS>                                      │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                  │
│     color_system = 'windows'                                                     │
│         encoding = 'utf-8'                                                       │
│             file = <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'> │
│           height = 57                                                            │
│    is_alt_screen = False                                                         │
│ is_dumb_terminal = False                                                         │
│   is_interactive = True                                                          │
│       is_jupyter = False                                                         │
│      is_terminal = True                                                          │
│   legacy_windows = True                                                          │
│         no_color = False                                                         │
│          options = ConsoleOptions(                                               │
│                        size=ConsoleDimensions(width=117, height=57),             │
│                        legacy_windows=True,                                      │
│                        min_width=1,                                              │
│                        max_width=117,                                            │
│                        is_terminal=True,                                         │
│                        encoding='utf-8',                                         │
│                        max_height=57,                                            │
│                        justify=None,                                             │
│                        overflow=None,                                            │
│                        no_wrap=False,                                            │
│                        highlight=None,                                           │
│                        markup=None,                                              │
│                        height=None                                               │
│                    )                                                             │
│            quiet = False                                                         │
│           record = False                                                         │
│         safe_box = True                                                          │
│             size = ConsoleDimensions(width=117, height=57)                       │
│        soft_wrap = False                                                         │
│           stderr = False                                                         │
│            style = None                                                          │
│         tab_size = 8                                                             │
│            width = 117                                                           │
└──────────────────────────────────────────────────────────────────────────────────┘
┌─── <class 'rich._windows.WindowsConsoleFeatures'> ────┐
│ Windows features available.                           │
│                                                       │
│ ┌───────────────────────────────────────────────────┐ │
│ │ WindowsConsoleFeatures(vt=False, truecolor=False) │ │
│ └───────────────────────────────────────────────────┘ │
│                                                       │
│ truecolor = False                                     │
│        vt = False                                     │
└───────────────────────────────────────────────────────┘
┌────── Environment Variables ───────┐
│ {                                  │
│     'TERM': 'cygwin',              │
│     'COLORTERM': None,             │
│     'CLICOLOR': None,              │
│     'NO_COLOR': None,              │
│     'TERM_PROGRAM': None,          │
│     'COLUMNS': None,               │
│     'LINES': None,                 │
│     'JPY_PARENT_PID': None,        │
│     'VSCODE_VERBOSE_LOGGING': None │
│ }                                  │
└────────────────────────────────────┘
platform="Windows"

Could not confirm either way (not considered a terminal)

  • (2*) Windows 8.1 Pro x64 + MinTTY 3.1.0 (x86_64-pc-msys) + Git Bash 4.4.23(1)-release (x86_64-pc-msys) + Python 3.9.13
Windows + MinTTY + Git Bash platform details

python -m rich and python -m rich.diagnose do not display any colors, many of the characters are mojibake/missing, and only work at all without an error when running in UTF-8 mode

$ python -m pip list | grep rich
rich               12.4.4

$ python -m rich.diagnose
+---------------------- <class 'rich.console.Console'> -----------------------+
| A high level console interface.                                             |
|                                                                             |
| +-------------------------------------------------------------------------+ |
| | <console width=79 None>                                                 | |
| +-------------------------------------------------------------------------+ |
|                                                                             |
|     color_system = None                                                     |
|         encoding = 'cp1252'                                                 |
|             file = <_io.TextIOWrapper name='<stdout>' mode='w'              |
|                    encoding='cp1252'>                                       |
|           height = 25                                                       |
|    is_alt_screen = False                                                    |
| is_dumb_terminal = False                                                    |
|   is_interactive = False                                                    |
|       is_jupyter = False                                                    |
|      is_terminal = False                                                    |
|   legacy_windows = True                                                     |
|         no_color = False                                                    |
|          options = ConsoleOptions(                                          |
|                        size=ConsoleDimensions(width=79, height=25),         |
|                        legacy_windows=True,                                 |
|                        min_width=1,                                         |
|                        max_width=79,                                        |
|                        is_terminal=False,                                   |
|                        encoding='cp1252',                                   |
|                        max_height=25,                                       |
|                        justify=None,                                        |
|                        overflow=None,                                       |
|                        no_wrap=False,                                       |
|                        highlight=None,                                      |
|                        markup=None,                                         |
|                        height=None                                          |
|                    )                                                        |
|            quiet = False                                                    |
|           record = False                                                    |
|         safe_box = True                                                     |
|             size = ConsoleDimensions(width=79, height=25)                   |
|        soft_wrap = False                                                    |
|           stderr = False                                                    |
|            style = None                                                     |
|         tab_size = 8                                                        |
|            width = 79                                                       |
+-----------------------------------------------------------------------------+
+--- <class 'rich._windows.WindowsConsoleFeatures'> ----+
| Windows features available.                           |
|                                                       |
| +---------------------------------------------------+ |
| | WindowsConsoleFeatures(vt=False, truecolor=False) | |
| +---------------------------------------------------+ |
|                                                       |
| truecolor = False                                     |
|        vt = False                                     |
+-------------------------------------------------------+
+------ Environment Variables -------+
| {                                  |
|     'TERM': 'xterm',               |
|     'COLORTERM': None,             |
|     'CLICOLOR': None,              |
|     'NO_COLOR': None,              |
|     'TERM_PROGRAM': None,          |
|     'COLUMNS': None,               |
|     'LINES': None,                 |
|     'JPY_PARENT_PID': None,        |
|     'VSCODE_VERBOSE_LOGGING': None |
| }                                  |
+------------------------------------+
platform="Windows"

$ python -X utf8 -m rich.diagnose
┌────────────────────── <class 'rich.console.Console'> ───────────────────────┐
│ A high level console interface.                                             │
│                                                                             │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ <console width=79 None>                                                 │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│                                                                             │
│     color_system = None                                                     │
│         encoding = 'utf-8'                                                  │
│             file = <_io.TextIOWrapper name='<stdout>' mode='w'              │
│                    encoding='utf-8'>                                        │
│           height = 25                                                       │
│    is_alt_screen = False                                                    │
│ is_dumb_terminal = False                                                    │
│   is_interactive = False                                                    │
│       is_jupyter = False                                                    │
│      is_terminal = False                                                    │
│   legacy_windows = True                                                     │
│         no_color = False                                                    │
│          options = ConsoleOptions(                                          │
│                        size=ConsoleDimensions(width=79, height=25),         │
│                        legacy_windows=True,                                 │
│                        min_width=1,                                         │
│                        max_width=79,                                        │
│                        is_terminal=False,                                   │
│                        encoding='utf-8',                                    │
│                        max_height=25,                                       │
│                        justify=None,                                        │
│                        overflow=None,                                       │
│                        no_wrap=False,                                       │
│                        highlight=None,                                      │
│                        markup=None,                                         │
│                        height=None                                          │
│                    )                                                        │
│            quiet = False                                                    │
│           record = False                                                    │
│         safe_box = True                                                     │
│             size = ConsoleDimensions(width=79, height=25)                   │
│        soft_wrap = False                                                    │
│           stderr = False                                                    │
│            style = None                                                     │
│         tab_size = 8                                                        │
│            width = 79                                                       │
└─────────────────────────────────────────────────────────────────────────────┘
┌─── <class 'rich._windows.WindowsConsoleFeatures'> ────┐
│ Windows features available.                           │
│                                                       │
│ ┌───────────────────────────────────────────────────┐ │
│ │ WindowsConsoleFeatures(vt=False, truecolor=False) │ │
│ └───────────────────────────────────────────────────┘ │
│                                                       │
│ truecolor = False                                     │
│        vt = False                                     │
└───────────────────────────────────────────────────────┘
┌────── Environment Variables ───────┐
│ {                                  │
│     'TERM': 'xterm',               │
│     'COLORTERM': None,             │
│     'CLICOLOR': None,              │
│     'NO_COLOR': None,              │
│     'TERM_PROGRAM': None,          │
│     'COLUMNS': None,               │
│     'LINES': None,                 │
│     'JPY_PARENT_PID': None,        │
│     'VSCODE_VERBOSE_LOGGING': None │
│ }                                  │
└────────────────────────────────────┘
platform="Windows"

Confirmed working correctly:

  • Ubuntu 20.04.4 LTS via SSH + ConEmu 210912 stable + GNU bash 5.0.17(1)-release (x86_64-pc-linux-gnu) + Python 3.8.10 + ssh from (1*) and (2*)
Ubuntu via (1*) platform details

python -m rich and python -m rich.diagnose display colors and other characters correctly, as far as I can tell.

$ python -m pip list | grep rich
rich               12.4.4

$ python -m rich.diagnose
╭───────────────────────── <class 'rich.console.Console'> ─────────────────────────╮
│ A high level console interface.                                                  │
│                                                                                  │
│ ╭──────────────────────────────────────────────────────────────────────────────╮ │
│ │ <console width=118 ColorSystem.STANDARD>                                     │ │
│ ╰──────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                  │
│     color_system = 'standard'                                                    │
│         encoding = 'utf-8'                                                       │
│             file = <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'> │
│           height = 57                                                            │
│    is_alt_screen = False                                                         │
│ is_dumb_terminal = False                                                         │
│   is_interactive = True                                                          │
│       is_jupyter = False                                                         │
│      is_terminal = True                                                          │
│   legacy_windows = False                                                         │
│         no_color = False                                                         │
│          options = ConsoleOptions(                                               │
│                        size=ConsoleDimensions(width=118, height=57),             │
│                        legacy_windows=False,                                     │
│                        min_width=1,                                              │
│                        max_width=118,                                            │
│                        is_terminal=True,                                         │
│                        encoding='utf-8',                                         │
│                        max_height=57,                                            │
│                        justify=None,                                             │
│                        overflow=None,                                            │
│                        no_wrap=False,                                            │
│                        highlight=None,                                           │
│                        markup=None,                                              │
│                        height=None                                               │
│                    )                                                             │
│            quiet = False                                                         │
│           record = False                                                         │
│         safe_box = True                                                          │
│             size = ConsoleDimensions(width=118, height=57)                       │
│        soft_wrap = False                                                         │
│           stderr = False                                                         │
│            style = None                                                          │
│         tab_size = 8                                                             │
│            width = 118                                                           │
╰──────────────────────────────────────────────────────────────────────────────────╯
╭─── <class 'rich._windows.WindowsConsoleFeatures'> ────╮
│ Windows features available.                           │
│                                                       │
│ ╭───────────────────────────────────────────────────╮ │
│ │ WindowsConsoleFeatures(vt=False, truecolor=False) │ │
│ ╰───────────────────────────────────────────────────╯ │
│                                                       │
│ truecolor = False                                     │
│        vt = False                                     │
╰───────────────────────────────────────────────────────╯
╭────── Environment Variables ───────╮
│ {                                  │
│     'TERM': 'cygwin',              │
│     'COLORTERM': None,             │
│     'CLICOLOR': None,              │
│     'NO_COLOR': None,              │
│     'TERM_PROGRAM': None,          │
│     'COLUMNS': None,               │
│     'LINES': None,                 │
│     'JPY_PARENT_PID': None,        │
│     'VSCODE_VERBOSE_LOGGING': None │
│ }                                  │
╰────────────────────────────────────╯
platform="Linux"
Ubuntu via (2*) platform details

python -m rich and python -m rich.diagnose display colors and other characters correctly, as far as I can tell.

$ python -m pip list | grep rich
rich               12.4.4

$ python -m rich.diagnose
╭─────────────────────── <class 'rich.console.Console'> ───────────────────────╮
│ A high level console interface.                                              │
│                                                                              │
│ ╭──────────────────────────────────────────────────────────────────────────╮ │
│ │ <console width=80 ColorSystem.STANDARD>                                  │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│     color_system = 'standard'                                                │
│         encoding = 'utf-8'                                                   │
│             file = <_io.TextIOWrapper name='<stdout>' mode='w'               │
│                    encoding='utf-8'>                                         │
│           height = 24                                                        │
│    is_alt_screen = False                                                     │
│ is_dumb_terminal = False                                                     │
│   is_interactive = True                                                      │
│       is_jupyter = False                                                     │
│      is_terminal = True                                                      │
│   legacy_windows = False                                                     │
│         no_color = False                                                     │
│          options = ConsoleOptions(                                           │
│                        size=ConsoleDimensions(width=80, height=24),          │
│                        legacy_windows=False,                                 │
│                        min_width=1,                                          │
│                        max_width=80,                                         │
│                        is_terminal=True,                                     │
│                        encoding='utf-8',                                     │
│                        max_height=24,                                        │
│                        justify=None,                                         │
│                        overflow=None,                                        │
│                        no_wrap=False,                                        │
│                        highlight=None,                                       │
│                        markup=None,                                          │
│                        height=None                                           │
│                    )                                                         │
│            quiet = False                                                     │
│           record = False                                                     │
│         safe_box = True                                                      │
│             size = ConsoleDimensions(width=80, height=24)                    │
│        soft_wrap = False                                                     │
│           stderr = False                                                     │
│            style = None                                                      │
│         tab_size = 8                                                         │
│            width = 80                                                        │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─── <class 'rich._windows.WindowsConsoleFeatures'> ────╮
│ Windows features available.                           │
│                                                       │
│ ╭───────────────────────────────────────────────────╮ │
│ │ WindowsConsoleFeatures(vt=False, truecolor=False) │ │
│ ╰───────────────────────────────────────────────────╯ │
│                                                       │
│ truecolor = False                                     │
│        vt = False                                     │
╰───────────────────────────────────────────────────────╯
╭────── Environment Variables ───────╮
│ {                                  │
│     'TERM': 'xterm',               │
│     'COLORTERM': None,             │
│     'CLICOLOR': None,              │
│     'NO_COLOR': None,              │
│     'TERM_PROGRAM': None,          │
│     'COLUMNS': None,               │
│     'LINES': None,                 │
│     'JPY_PARENT_PID': None,        │
│     'VSCODE_VERBOSE_LOGGING': None │
│ }                                  │
╰────────────────────────────────────╯
platform="Linux"
@willmcgugan
Copy link
Collaborator

willmcgugan commented Jun 10, 2022

I see the issue. We're calling SetConsoleCursorInfo to hide the cursor. But this function also sets the cursor size.

         visible_cursor = CONSOLE_CURSOR_INFO(dwSize=100, bVisible=1)
        SetConsoleCursorInfo(self._handle, cursor_info=visible_cursor)

I assume what we need to do is to get the cursor info first, so that we don't clobber the dwSize field.

@github-actions
Copy link

Did I solve your problem?

Why not buy the devs a coffee to say thanks?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants