add --color option

This commit is contained in:
Anthony Sottile 2021-11-05 20:05:53 -04:00
parent 05cae7e046
commit 848003cc05
10 changed files with 181 additions and 2 deletions

View file

@ -0,0 +1,59 @@
"""ctypes hackery to enable color processing on windows.
See: https://github.com/pre-commit/pre-commit/blob/cb40e96/pre_commit/color.py
"""
import sys
if sys.platform == "win32": # pragma: no cover (windows)
def _enable() -> None:
from ctypes import POINTER
from ctypes import windll
from ctypes import WinError
from ctypes import WINFUNCTYPE
from ctypes.wintypes import BOOL
from ctypes.wintypes import DWORD
from ctypes.wintypes import HANDLE
STD_ERROR_HANDLE = -12
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
def bool_errcheck(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 = bool_errcheck
SetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, DWORD)(
("SetConsoleMode", windll.kernel32),
((1, "hConsoleHandle"), (1, "dwMode")),
)
SetConsoleMode.errcheck = bool_errcheck
# As of Windows 10, the Windows console supports (some) ANSI escape
# sequences, but it needs to be enabled using `SetConsoleMode` first.
#
# More info on the escape sequences supported:
# https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx
stderr = GetStdHandle(STD_ERROR_HANDLE)
flags = GetConsoleMode(stderr)
SetConsoleMode(stderr, flags | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
try:
_enable()
except OSError:
terminal_supports_color = False
else:
terminal_supports_color = True
else: # pragma: win32 no cover
terminal_supports_color = True

View file

@ -8,6 +8,8 @@ from typing import Optional
from typing import Tuple
from typing import TYPE_CHECKING
from flake8.formatting import _windows_color
if TYPE_CHECKING:
from flake8.statistics import Statistics
from flake8.style_guide import Violation
@ -51,6 +53,11 @@ class BaseFormatter:
self.filename = options.output_file
self.output_fd: Optional[IO[str]] = None
self.newline = "\n"
self.color = options.color == "always" or (
options.color == "auto"
and sys.stdout.isatty()
and _windows_color.terminal_supports_color
)
self.after_init()
def after_init(self) -> None:

View file

@ -8,6 +8,20 @@ from flake8.formatting import base
if TYPE_CHECKING:
from flake8.style_guide import Violation
COLORS = {
"bold": "\033[1m",
"black": "\033[30m",
"red": "\033[31m",
"green": "\033[32m",
"yellow": "\033[33m",
"blue": "\033[34m",
"magenta": "\033[35m",
"cyan": "\033[36m",
"white": "\033[37m",
"reset": "\033[m",
}
COLORS_OFF = {k: "" for k in COLORS}
class SimpleFormatter(base.BaseFormatter):
"""Simple abstraction for Default and Pylint formatter commonality.
@ -39,6 +53,7 @@ class SimpleFormatter(base.BaseFormatter):
"path": error.filename,
"row": error.line_number,
"col": error.column_number,
**(COLORS if self.color else COLORS_OFF),
}
@ -49,7 +64,11 @@ class Default(SimpleFormatter):
format string.
"""
error_format = "%(path)s:%(row)d:%(col)d: %(code)s %(text)s"
error_format = (
"%(bold)s%(path)s%(reset)s"
"%(cyan)s:%(reset)s%(row)d%(cyan)s:%(reset)s%(col)d%(cyan)s:%(reset)s "
"%(bold)s%(red)s%(code)s%(reset)s %(text)s"
)
def after_init(self) -> None:
"""Check for a custom format string."""

View file

@ -91,6 +91,7 @@ def register_default_options(option_manager):
The default options include:
- ``-q``/``--quiet``
- ``--color``
- ``--count``
- ``--diff``
- ``--exclude``
@ -118,7 +119,6 @@ def register_default_options(option_manager):
"""
add_option = option_manager.add_option
# pep8 options
add_option(
"-q",
"--quiet",
@ -128,6 +128,13 @@ def register_default_options(option_manager):
help="Report only file names, or nothing. This option is repeatable.",
)
add_option(
"--color",
choices=("auto", "always", "never"),
default="auto",
help="Whether to use color in output. Defaults to `%(default)s`.",
)
add_option(
"--count",
action="store_true",

View file

@ -269,6 +269,12 @@ class PluginManager: # pylint: disable=too-few-public-methods
"flake8>=3.7 (which implements per-file-ignores itself)."
)
continue
elif entry_point.name == "flake8-colors":
LOG.warning(
"flake8-colors plugin is incompatible with "
"flake8>=4.1 (which implements colors itself)."
)
continue
self._load_plugin_from_entrypoint(entry_point)
def _load_plugin_from_entrypoint(self, entry_point, local=False):