38
38
# error location when printing source code snippet.
39
39
MINIMUM_WIDTH = 20
40
40
41
+ # VT100 color code processing was added in Windows 10, but only the second major update,
42
+ # Threshold 2. Fortunately, everyone (even on LTSB, Long Term Support Branch) should
43
+ # have a version of Windows 10 newer than this. Note that Windows 8 and below are not
44
+ # supported, but are either going out of support, or make up only a few % of the market.
45
+ MINIMUM_WINDOWS_MAJOR_VT100 = 10
46
+ MINIMUM_WINDOWS_BUILD_VT100 = 10586
47
+
41
48
default_python2_interpreter = \
42
49
['python2' , 'python' , '/usr/bin/python' , 'C:\\ Python27\\ python.exe' ] # type: Final
43
50
@@ -467,30 +474,68 @@ class FancyFormatter:
467
474
def __init__ (self , f_out : IO [str ], f_err : IO [str ], show_error_codes : bool ) -> None :
468
475
self .show_error_codes = show_error_codes
469
476
# Check if we are in a human-facing terminal on a supported platform.
470
- if sys .platform not in ('linux' , 'darwin' ):
477
+ if sys .platform not in ('linux' , 'darwin' , 'win32' ):
471
478
self .dummy_term = True
472
479
return
473
480
force_color = int (os .getenv ('MYPY_FORCE_COLOR' , '0' ))
474
481
if not force_color and (not f_out .isatty () or not f_err .isatty ()):
475
482
self .dummy_term = True
476
483
return
477
-
478
- # We in a human-facing terminal, check if it supports enough styling.
484
+ if sys .platform == 'win32' :
485
+ self .dummy_term = not self .initialize_win_colors ()
486
+ else :
487
+ self .dummy_term = not self .initialize_unix_colors ()
488
+ if not self .dummy_term :
489
+ self .colors = {'red' : self .RED , 'green' : self .GREEN ,
490
+ 'blue' : self .BLUE , 'yellow' : self .YELLOW ,
491
+ 'none' : '' }
492
+
493
+ def initialize_win_colors (self ) -> bool :
494
+ """Return True if initialization was successful and we can use colors, False otherwise"""
495
+ # Windows ANSI escape sequences are only supported on Threshold 2 and above.
496
+ # we check with an assert at runtime and an if check for mypy, as asserts do not
497
+ # yet narrow platform
498
+ assert sys .platform == 'win32'
499
+ if sys .platform == 'win32' :
500
+ winver = sys .getwindowsversion ()
501
+ if (winver .major < MINIMUM_WINDOWS_MAJOR_VT100
502
+ or winver .build < MINIMUM_WINDOWS_BUILD_VT100 ):
503
+ return False
504
+ import ctypes
505
+ kernel32 = ctypes .windll .kernel32
506
+ ENABLE_PROCESSED_OUTPUT = 0x1
507
+ ENABLE_WRAP_AT_EOL_OUTPUT = 0x2
508
+ ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4
509
+ STD_OUTPUT_HANDLE = - 11
510
+ kernel32 .SetConsoleMode (kernel32 .GetStdHandle (STD_OUTPUT_HANDLE ),
511
+ ENABLE_PROCESSED_OUTPUT
512
+ | ENABLE_WRAP_AT_EOL_OUTPUT
513
+ | ENABLE_VIRTUAL_TERMINAL_PROCESSING )
514
+ self .BOLD = '\033 [1m'
515
+ self .UNDER = '\033 [4m'
516
+ self .BLUE = '\033 [94m'
517
+ self .GREEN = '\033 [92m'
518
+ self .RED = '\033 [91m'
519
+ self .YELLOW = '\033 [93m'
520
+ self .NORMAL = '\033 [0m'
521
+ self .DIM = '\033 [2m'
522
+ return True
523
+ return False
524
+
525
+ def initialize_unix_colors (self ) -> bool :
526
+ """Return True if initialization was successful and we can use colors, False otherwise"""
479
527
if not CURSES_ENABLED :
480
- self .dummy_term = True
481
- return
528
+ return False
482
529
try :
483
530
curses .setupterm ()
484
531
except curses .error :
485
532
# Most likely terminfo not found.
486
- self .dummy_term = True
487
- return
533
+ return False
488
534
bold = curses .tigetstr ('bold' )
489
535
under = curses .tigetstr ('smul' )
490
536
set_color = curses .tigetstr ('setaf' )
491
- self .dummy_term = not (bold and under and set_color )
492
- if self .dummy_term :
493
- return
537
+ if not (bold and under and set_color ):
538
+ return False
494
539
495
540
self .NORMAL = curses .tigetstr ('sgr0' ).decode ()
496
541
self .BOLD = bold .decode ()
@@ -503,10 +548,7 @@ def __init__(self, f_out: IO[str], f_err: IO[str], show_error_codes: bool) -> No
503
548
self .GREEN = curses .tparm (set_color , curses .COLOR_GREEN ).decode ()
504
549
self .RED = curses .tparm (set_color , curses .COLOR_RED ).decode ()
505
550
self .YELLOW = curses .tparm (set_color , curses .COLOR_YELLOW ).decode ()
506
-
507
- self .colors = {'red' : self .RED , 'green' : self .GREEN ,
508
- 'blue' : self .BLUE , 'yellow' : self .YELLOW ,
509
- 'none' : '' }
551
+ return True
510
552
511
553
def style (self , text : str , color : Literal ['red' , 'green' , 'blue' , 'yellow' , 'none' ],
512
554
bold : bool = False , underline : bool = False , dim : bool = False ) -> str :
0 commit comments