28

The latest Windows 10 updates include support for ANSI escape sequences in conhost.exe.

enter image description here

I have been able to confirm that the escape sequences are properly picked up in cmd.exe, so I have the necessary updates. In particular, I tried typing in prompt $e[?25l, which hides the cursor, and then prompt $e[?25h, which again shows the cursor.

However, if I start a Python interpreter, and then do the following:

>>> import sys
>>> sys.stdout.write("\033[?25l")

Well, the cursor isn't hidden. How can I set things up the right way so that the console is able to get escape sequences from Python?

Eryk Sun
  • 33,190
  • 5
  • 92
  • 111
bzm3r
  • 3,113
  • 6
  • 34
  • 67

4 Answers4

41

The problem is that the Python interpreter doesn't enable the processing of ANSI escape sequences. The ANSI sequences work from the Windows command prompt because cmd does enable them. If you start Python from the command prompt you'll find the ANSI sequences do work, including the ones for enabling and disabling the cursor. That's because cmd has already enabled them for that console window.

If you want have something you can click on to start the Python interpreter with ANSI escapes enabled you can create a shortcut that runs a command something like cmd /c C:\PythonXY\python.

Another, harder, solution would be to use ctypes to enable ANSI escape sequence processing for the console window by calling the SetConsoleMode Windows API with the ENABLE_VIRTUAL_TERMINAL_PROCESSING flag set. For example:

import ctypes

kernel32 = ctypes.windll.kernel32
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
Ross Ridge
  • 38,414
  • 7
  • 81
  • 112
  • Hi Ross -- thanks for this really nice answer :) It seems you do a lot of console related stuff, and I am wondering -- do you also know how to achieve non-blocking input in a Windows console, similar to how one might do it in Linux, by using the O_NONBLOCK flag? Python Prompt Toolkit achieves non-blocking input by using a special read function exposed by the Windows API: https://github.com/jonathanslenders/python-prompt-toolkit/blob/master/prompt_toolkit/terminal/win32_input.py#L99 -- there doesn't seem to be a flag I can set, which would then allow `sys.stdin.read()` to not block? – bzm3r Apr 21 '16 at 17:23
  • @user89 No, unfortunately there's no equivalent to the O_NONBLOCK flag. Windows uses a different model involving waiting on events on handles and overlapped I/O (the later isn't supported for console handles). You'll either need to delve into the Windows API through ctypes or let something like curses handle it for you. Curses support isn't shipped with Python for Windows, but you can download a port separately, see: https://docs.python.org/3.3/howto/curses.html – Ross Ridge Apr 21 '16 at 18:15
  • Makes sense -- I guess I'll just wait to see how the Windows console improves (especially with the upcoming release of bash on Windows), and for now stick to using VMs with Linux in order to run+develop console apps. – bzm3r Apr 21 '16 at 18:30
  • @user89 Bash for Windows is part of their new Linux subsystem for Windows. Unfortunately, it's little better integrated than using VMs with Linux, the file system is shared but that's it. You might look at Cygwin instead, which in addition to letting you run Windows programs from bash and "Linux" programs from the Windows command line, it works on current version of Windows 10, along with older versions of Windows. – Ross Ridge Apr 21 '16 at 18:48
  • Cygwin doesn't have support for flags like O_NONBLOCK either though: http://stackoverflow.com/questions/34696267/python-non-blocking-read-from-file-stream-in-cygwin -- but maybe I am not understanding something right? – bzm3r Apr 21 '16 at 20:28
  • @user89 I'm guessing that person was trying to use the native Windows port of Python under Cygwin rather than Cygwin port. The O_NONBLOCK flag exists in the Cygwin port. For example this works: `print os.open("/dev/tty", os.O_NONBLOCK | os.O_RDWR, 0)`. – Ross Ridge Apr 21 '16 at 20:39
  • (that person was me :p) Wow, that's absolutely awesome! I did not know this about Cygwin! Thank you so much! – bzm3r Apr 23 '16 at 18:40
  • @RossRidge Sorry for my ignorance, why the number "7"? – Henrique Dias Feb 27 '17 at 22:53
  • 7
    @HenriqueDias It's the equivalent of `ENABLE_PROCESSED_OUTPUT|ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_VIRTUAL_TERMINAL_PROCESSING` which is the equivalent of `0x0001|0x0002|0x0004` which equals 7. Python can't include the C header file that defines these flags, so I've just substituted the number 7 that they end up evaluating to. The first two flags are on by default and enable the normal console output processing that you would expect, and the third is the flag that enables processing of ANSI escape sequences and isn't enabled by default. – Ross Ridge Feb 27 '17 at 23:24
  • @RossRidge thanks a lot! I was trying to do the same as you did but in Go and I found this post and your answer. Then I tried with 7 and it worked (I've been trying with 0x0004 before). I just didn't know why 7. Thanks :) – Henrique Dias Feb 28 '17 at 14:55
5

This adaptation of some code I proposed here should help get you started. Enables ANSI VT mode (virtual terminal processing) on Windows 10. Pass in argument value 1 for stdout or 2 stderr.

def _windows_enable_ANSI(std_id):
    """Enable Windows 10 cmd.exe ANSI VT Virtual Terminal Processing."""
    from ctypes import byref, POINTER, windll, WINFUNCTYPE
    from ctypes.wintypes import BOOL, DWORD, HANDLE

    GetStdHandle = WINFUNCTYPE(
        HANDLE,
        DWORD)(('GetStdHandle', windll.kernel32))

    GetFileType = WINFUNCTYPE(
        DWORD,
        HANDLE)(('GetFileType', windll.kernel32))

    GetConsoleMode = WINFUNCTYPE(
        BOOL,
        HANDLE,
        POINTER(DWORD))(('GetConsoleMode', windll.kernel32))

    SetConsoleMode = WINFUNCTYPE(
        BOOL,
        HANDLE,
        DWORD)(('SetConsoleMode', windll.kernel32))

    if std_id == 1:       # stdout
        h = GetStdHandle(-11)
    elif std_id == 2:     # stderr
        h = GetStdHandle(-12)
    else:
        return False

    if h is None or h == HANDLE(-1):
        return False

    FILE_TYPE_CHAR = 0x0002
    if (GetFileType(h) & 3) != FILE_TYPE_CHAR:
        return False

    mode = DWORD()
    if not GetConsoleMode(h, byref(mode)):
        return False

    ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
    if (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0:
        SetConsoleMode(h, mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
    return True
Catherine
  • 22,492
  • 3
  • 32
  • 47
Glenn Slayden
  • 17,543
  • 3
  • 114
  • 108
  • 1
    It doesn't work, gives "NameError: name 'compat_ctypes_WINFUNCTYPE' is not defined" – barteks2x Feb 13 '19 at 22:54
  • 1
    As the author mentioned, this code came from a GitHub issue comment regarding youtube-dl. The missing function is defined in youtube-dl (you can use the GitHub search functions to find it there). If you are not using PyPy, then `compat_ctypes_WINFUNCTYPE` = `WINFUNCTYPE` so you can replace those calls accordingly. – mhucka Aug 19 '19 at 02:49
4

The colorama package enables ANSI codes in Windows.

Usage:

from colorama import init
init()

After that ANSI codes should work.

jtpereyda
  • 6,987
  • 10
  • 51
  • 80
3

Use the solution suggested here: https://stackoverflow.com/a/293633/18487576

Run:

os.system('color')

to enable color mode on the terminal.

I was using the accepted answer with ctypes until I discovered this. This solution does not require any additional installations or native API chicanery.

This command is not an executable file, but a specific command within the Windows cmd terminal.

https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/color

Its official use is to change foreground/background colors of the shell. The fact that it will activate ANSI/VT100 is apparently undocumented.

bdavis
  • 91
  • 3