Merge pull request #144 from willmcgugan/windows-color

detect windows
This commit is contained in:
Will McGugan 2020-07-07 17:08:59 +01:00 committed by GitHub
commit 038e22eb98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 123 additions and 16 deletions

View File

@ -1,5 +1,6 @@
[run]
omit = rich/jupyter.py
rich/_windows.py
[report]
exclude_lines =

View File

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.0.4] - 2020-07-07
### Changed
- More precise detection of Windows console https://github.com/willmcgugan/rich/issues/140
## [3.0.3] - 2020-07-03
### Fixed

View File

@ -2,7 +2,7 @@
name = "rich"
homepage = "https://github.com/willmcgugan/rich"
documentation = "https://rich.readthedocs.io/en/latest/"
version = "3.0.3"
version = "3.0.4"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
authors = ["Will McGugan <willmcgugan@gmail.com>"]
license = "MIT"

70
rich/_windows.py Normal file
View File

@ -0,0 +1,70 @@
import sys
from dataclasses import dataclass
@dataclass
class WindowsConsoleFeatures:
"""Windows features available."""
vt: bool = False
truecolor: bool = False
try:
import ctypes
from ctypes import wintypes
from ctypes import LibraryLoader
windll = LibraryLoader(ctypes.WinDLL) # type: ignore
except (AttributeError, ImportError, ValueError):
# Fallback if we can't load the Windows DLL
def get_windows_console_features() -> WindowsConsoleFeatures:
features = WindowsConsoleFeatures()
return features
else:
STDOUT = -11
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
_GetConsoleMode = windll.kernel32.GetConsoleMode
_GetConsoleMode.argtypes = [wintypes.HANDLE, wintypes.LPDWORD]
_GetConsoleMode.restype = wintypes.BOOL
_GetStdHandle = windll.kernel32.GetStdHandle
_GetStdHandle.argtypes = [
wintypes.DWORD,
]
_GetStdHandle.restype = wintypes.HANDLE
def get_windows_console_features() -> WindowsConsoleFeatures:
"""Get windows console features.
Returns:
WindowsConsoleFeatures: An instance of WindowsConsoleFeatures.
"""
handle = _GetStdHandle(STDOUT)
console_mode = wintypes.DWORD()
result = _GetConsoleMode(handle, console_mode)
vt = bool(result and console_mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
truecolor = False
if vt:
win_version = sys.getwindowsversion()
truecolor = win_version.major > 10 or (
win_version.major == 10 and win_version.build > 15063
)
features = WindowsConsoleFeatures(vt=vt, truecolor=truecolor)
return features
if __name__ == "__main__":
import platform
features = get_windows_console_features()
from rich import print
print(f'platform="{platform.system()}"')
print(repr(features))

View File

@ -21,6 +21,7 @@ from typing import (
Iterable,
List,
Optional,
TYPE_CHECKING,
NamedTuple,
Union,
)
@ -44,6 +45,8 @@ from .segment import Segment
from .text import Text, TextType
from .theme import Theme
if TYPE_CHECKING:
from ._windows import WindowsConsoleFeatures
WINDOWS = platform.system() == "Windows"
@ -244,9 +247,22 @@ class RenderHook:
return renderables
_windows_console_features: Optional["WindowsConsoleFeatures"] = None
def get_windows_console_features() -> "WindowsConsoleFeatures": # pragma: no cover
global _windows_console_features
if _windows_console_features is not None:
return _windows_console_features
from ._windows import get_windows_console_features
_windows_console_features = get_windows_console_features()
return _windows_console_features
def detect_legacy_windows() -> bool:
"""Detect legacy Windows."""
return "WINDIR" in os.environ and "WT_SESSION" not in os.environ
return WINDOWS and not get_windows_console_features().vt
if detect_legacy_windows(): # pragma: no cover
@ -364,19 +380,26 @@ class Console:
def _detect_color_system(self) -> Optional[ColorSystem]:
"""Detect color system from env vars."""
if not self.is_terminal:
return None
if self.legacy_windows: # pragma: no cover
return ColorSystem.WINDOWS
if "WT_SESSION" in os.environ:
# Exception for Windows terminal
if self.is_jupyter:
return ColorSystem.TRUECOLOR
color_term = os.environ.get("COLORTERM", "").strip().lower()
return (
ColorSystem.TRUECOLOR
if color_term in ("truecolor", "24bit")
else ColorSystem.EIGHT_BIT
)
if not self.is_terminal or "NO_COLOR" in os.environ:
return None
if WINDOWS: # pragma: no cover
if self.legacy_windows: # pragma: no cover
return ColorSystem.WINDOWS
windows_console_features = get_windows_console_features()
return (
ColorSystem.TRUECOLOR
if windows_console_features.truecolor
else ColorSystem.EIGHT_BIT
)
else:
color_term = os.environ.get("COLORTERM", "").strip().lower()
return (
ColorSystem.TRUECOLOR
if color_term in ("truecolor", "24bit")
else ColorSystem.EIGHT_BIT
)
def _enter_buffer(self) -> None:
"""Enter in to a buffer context, and buffer all output."""
@ -921,7 +944,7 @@ class Console:
"""Check if the buffer may be rendered."""
with self._lock:
if self._buffer_index == 0:
if self.is_jupyter:
if self.is_jupyter: # pragma: no cover
from .jupyter import display
display(self._buffer)

View File

@ -73,7 +73,7 @@ class ReprHighlighter(RegexHighlighter):
highlights = [
r"(?P<brace>[\{\[\(\)\]\}])",
r"(?P<tag_start>\<)(?P<tag_name>\w*)(?P<tag_contents>.*?)(?P<tag_end>\>)",
r"(?P<attrib_name>\w+?)=(?P<attrib_value>\"?\S+\"?)",
r"(?P<attrib_name>\w+?)=(?P<attrib_value>\"?[\w_]+\"?)",
r"(?P<bool_true>True)|(?P<bool_false>False)|(?P<none>None)",
r"(?P<number>(?<!\w)\-?[0-9]+\.?[0-9]*\b)",
r"(?P<number>0x[0-9a-f]*)",

7
tests/test_jupyter.py Normal file
View File

@ -0,0 +1,7 @@
from rich.console import Console
def test_jupyter():
console = Console(force_jupyter=True)
assert console.width == 93
assert console.color_system == "truecolor"