mirror of
https://github.com/godotengine/godot.git
synced 2025-01-22 10:32:54 -05:00
130 lines
4 KiB
Python
130 lines
4 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
from enum import Enum
|
|
from typing import Final
|
|
|
|
# Colors are disabled in non-TTY environments such as pipes. This means if output is redirected
|
|
# to a file, it won't contain color codes. Colors are always enabled on continuous integration.
|
|
|
|
IS_CI: Final[bool] = bool(os.environ.get("CI"))
|
|
STDOUT_TTY: Final[bool] = bool(sys.stdout.isatty())
|
|
STDERR_TTY: Final[bool] = bool(sys.stderr.isatty())
|
|
|
|
|
|
def _color_supported(stdout: bool) -> bool:
|
|
"""
|
|
Validates if the current environment supports colored output. Attempts to enable ANSI escape
|
|
code support on Windows 10 and later.
|
|
"""
|
|
if IS_CI:
|
|
return True
|
|
|
|
if sys.platform != "win32":
|
|
return STDOUT_TTY if stdout else STDERR_TTY
|
|
else:
|
|
from ctypes import POINTER, WINFUNCTYPE, WinError, windll
|
|
from ctypes.wintypes import BOOL, DWORD, HANDLE
|
|
|
|
STD_HANDLE = -11 if stdout else -12
|
|
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
|
|
|
|
def err_handler(result, func, args):
|
|
if not result:
|
|
raise WinError()
|
|
return args
|
|
|
|
GetStdHandle = WINFUNCTYPE(HANDLE, DWORD)(("GetStdHandle", windll.kernel32), ((1, "nStdHandle"),))
|
|
GetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, POINTER(DWORD))(
|
|
("GetConsoleMode", windll.kernel32),
|
|
((1, "hConsoleHandle"), (2, "lpMode")),
|
|
)
|
|
GetConsoleMode.errcheck = err_handler
|
|
SetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, DWORD)(
|
|
("SetConsoleMode", windll.kernel32),
|
|
((1, "hConsoleHandle"), (1, "dwMode")),
|
|
)
|
|
SetConsoleMode.errcheck = err_handler
|
|
|
|
try:
|
|
handle = GetStdHandle(STD_HANDLE)
|
|
flags = GetConsoleMode(handle)
|
|
SetConsoleMode(handle, flags | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
|
return True
|
|
except OSError:
|
|
return False
|
|
|
|
|
|
STDOUT_COLOR: Final[bool] = _color_supported(True)
|
|
STDERR_COLOR: Final[bool] = _color_supported(False)
|
|
_stdout_override: bool = STDOUT_COLOR
|
|
_stderr_override: bool = STDERR_COLOR
|
|
|
|
|
|
def toggle_color(stdout: bool, value: bool | None = None) -> None:
|
|
"""
|
|
Explicitly toggle color codes, regardless of support.
|
|
|
|
- `stdout`: A boolean to choose the output stream. `True` for stdout, `False` for stderr.
|
|
- `value`: An optional boolean to explicitly set the color state instead of toggling.
|
|
"""
|
|
if stdout:
|
|
global _stdout_override
|
|
_stdout_override = value if value is not None else not _stdout_override
|
|
else:
|
|
global _stderr_override
|
|
_stderr_override = value if value is not None else not _stderr_override
|
|
|
|
|
|
class Ansi(Enum):
|
|
"""
|
|
Enum class for adding ansi codepoints directly into strings. Automatically converts values to
|
|
strings representing their internal value.
|
|
"""
|
|
|
|
RESET = "\x1b[0m"
|
|
|
|
BOLD = "\x1b[1m"
|
|
DIM = "\x1b[2m"
|
|
ITALIC = "\x1b[3m"
|
|
UNDERLINE = "\x1b[4m"
|
|
STRIKETHROUGH = "\x1b[9m"
|
|
REGULAR = "\x1b[22;23;24;29m"
|
|
|
|
BLACK = "\x1b[30m"
|
|
RED = "\x1b[31m"
|
|
GREEN = "\x1b[32m"
|
|
YELLOW = "\x1b[33m"
|
|
BLUE = "\x1b[34m"
|
|
MAGENTA = "\x1b[35m"
|
|
CYAN = "\x1b[36m"
|
|
WHITE = "\x1b[37m"
|
|
GRAY = "\x1b[90m"
|
|
|
|
def __str__(self) -> str:
|
|
return self.value
|
|
|
|
|
|
def print_info(*values: object) -> None:
|
|
"""Prints a informational message with formatting."""
|
|
if _stdout_override:
|
|
print(f"{Ansi.GRAY}{Ansi.BOLD}INFO:{Ansi.REGULAR}", *values, Ansi.RESET)
|
|
else:
|
|
print("INFO:", *values)
|
|
|
|
|
|
def print_warning(*values: object) -> None:
|
|
"""Prints a warning message with formatting."""
|
|
if _stderr_override:
|
|
print(f"{Ansi.YELLOW}{Ansi.BOLD}WARNING:{Ansi.REGULAR}", *values, Ansi.RESET, file=sys.stderr)
|
|
else:
|
|
print("WARNING:", *values, file=sys.stderr)
|
|
|
|
|
|
def print_error(*values: object) -> None:
|
|
"""Prints an error message with formatting."""
|
|
if _stderr_override:
|
|
print(f"{Ansi.RED}{Ansi.BOLD}ERROR:{Ansi.REGULAR}", *values, Ansi.RESET, file=sys.stderr)
|
|
else:
|
|
print("ERROR:", *values, file=sys.stderr)
|