Merge pull request #12443 from bluetech/pygments-refactor
terminalwriter: small refactor of pygments code, improve usage errors
This commit is contained in:
commit
f3946dce07
|
@ -8,11 +8,17 @@ from typing import Literal
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
from typing import TextIO
|
from typing import TextIO
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from ..compat import assert_never
|
from ..compat import assert_never
|
||||||
from .wcwidth import wcswidth
|
from .wcwidth import wcswidth
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from pygments.formatter import Formatter
|
||||||
|
from pygments.lexer import Lexer
|
||||||
|
|
||||||
|
|
||||||
# This code was initially copied from py 1.8.1, file _io/terminalwriter.py.
|
# This code was initially copied from py 1.8.1, file _io/terminalwriter.py.
|
||||||
|
|
||||||
|
|
||||||
|
@ -194,58 +200,76 @@ class TerminalWriter:
|
||||||
for indent, new_line in zip(indents, new_lines):
|
for indent, new_line in zip(indents, new_lines):
|
||||||
self.line(indent + new_line)
|
self.line(indent + new_line)
|
||||||
|
|
||||||
def _highlight(
|
def _get_pygments_lexer(
|
||||||
self, source: str, lexer: Literal["diff", "python"] = "python"
|
self, lexer: Literal["python", "diff"]
|
||||||
) -> str:
|
) -> Optional["Lexer"]:
|
||||||
"""Highlight the given source if we have markup support."""
|
try:
|
||||||
|
if lexer == "python":
|
||||||
|
from pygments.lexers.python import PythonLexer
|
||||||
|
|
||||||
|
return PythonLexer()
|
||||||
|
elif lexer == "diff":
|
||||||
|
from pygments.lexers.diff import DiffLexer
|
||||||
|
|
||||||
|
return DiffLexer()
|
||||||
|
else:
|
||||||
|
assert_never(lexer)
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _get_pygments_formatter(self) -> Optional["Formatter"]:
|
||||||
|
try:
|
||||||
|
import pygments.util
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
return None
|
||||||
|
|
||||||
from _pytest.config.exceptions import UsageError
|
from _pytest.config.exceptions import UsageError
|
||||||
|
|
||||||
if not source or not self.hasmarkup or not self.code_highlight:
|
theme = os.getenv("PYTEST_THEME")
|
||||||
return source
|
theme_mode = os.getenv("PYTEST_THEME_MODE", "dark")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from pygments.formatters.terminal import TerminalFormatter
|
from pygments.formatters.terminal import TerminalFormatter
|
||||||
|
|
||||||
if lexer == "python":
|
return TerminalFormatter(bg=theme_mode, style=theme)
|
||||||
from pygments.lexers.python import PythonLexer as Lexer
|
|
||||||
elif lexer == "diff":
|
|
||||||
from pygments.lexers.diff import DiffLexer as Lexer
|
|
||||||
else:
|
|
||||||
assert_never(lexer)
|
|
||||||
from pygments import highlight
|
|
||||||
import pygments.util
|
|
||||||
except ImportError:
|
|
||||||
return source
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
highlighted: str = highlight(
|
|
||||||
source,
|
|
||||||
Lexer(),
|
|
||||||
TerminalFormatter(
|
|
||||||
bg=os.getenv("PYTEST_THEME_MODE", "dark"),
|
|
||||||
style=os.getenv("PYTEST_THEME"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
# pygments terminal formatter may add a newline when there wasn't one.
|
|
||||||
# We don't want this, remove.
|
|
||||||
if highlighted[-1] == "\n" and source[-1] != "\n":
|
|
||||||
highlighted = highlighted[:-1]
|
|
||||||
|
|
||||||
# Some lexers will not set the initial color explicitly
|
except pygments.util.ClassNotFound as e:
|
||||||
# which may lead to the previous color being propagated to the
|
raise UsageError(
|
||||||
# start of the expression, so reset first.
|
f"PYTEST_THEME environment variable has an invalid value: '{theme}'. "
|
||||||
return "\x1b[0m" + highlighted
|
"Hint: See available pygments styles with `pygmentize -L styles`."
|
||||||
except pygments.util.ClassNotFound as e:
|
) from e
|
||||||
raise UsageError(
|
except pygments.util.OptionError as e:
|
||||||
"PYTEST_THEME environment variable had an invalid value: '{}'. "
|
raise UsageError(
|
||||||
"Only valid pygment styles are allowed.".format(
|
f"PYTEST_THEME_MODE environment variable has an invalid value: '{theme_mode}'. "
|
||||||
os.getenv("PYTEST_THEME")
|
"The allowed values are 'dark' (default) and 'light'."
|
||||||
)
|
) from e
|
||||||
) from e
|
|
||||||
except pygments.util.OptionError as e:
|
def _highlight(
|
||||||
raise UsageError(
|
self, source: str, lexer: Literal["diff", "python"] = "python"
|
||||||
"PYTEST_THEME_MODE environment variable had an invalid value: '{}'. "
|
) -> str:
|
||||||
"The only allowed values are 'dark' and 'light'.".format(
|
"""Highlight the given source if we have markup support."""
|
||||||
os.getenv("PYTEST_THEME_MODE")
|
if not source or not self.hasmarkup or not self.code_highlight:
|
||||||
)
|
return source
|
||||||
) from e
|
|
||||||
|
pygments_lexer = self._get_pygments_lexer(lexer)
|
||||||
|
if pygments_lexer is None:
|
||||||
|
return source
|
||||||
|
|
||||||
|
pygments_formatter = self._get_pygments_formatter()
|
||||||
|
if pygments_formatter is None:
|
||||||
|
return source
|
||||||
|
|
||||||
|
from pygments import highlight
|
||||||
|
|
||||||
|
highlighted: str = highlight(source, pygments_lexer, pygments_formatter)
|
||||||
|
# pygments terminal formatter may add a newline when there wasn't one.
|
||||||
|
# We don't want this, remove.
|
||||||
|
if highlighted[-1] == "\n" and source[-1] != "\n":
|
||||||
|
highlighted = highlighted[:-1]
|
||||||
|
|
||||||
|
# Some lexers will not set the initial color explicitly
|
||||||
|
# which may lead to the previous color being propagated to the
|
||||||
|
# start of the expression, so reset first.
|
||||||
|
highlighted = "\x1b[0m" + highlighted
|
||||||
|
|
||||||
|
return highlighted
|
||||||
|
|
|
@ -2609,8 +2609,8 @@ class TestCodeHighlight:
|
||||||
monkeypatch.setenv("PYTEST_THEME", "invalid")
|
monkeypatch.setenv("PYTEST_THEME", "invalid")
|
||||||
result = pytester.runpytest_subprocess("--color=yes")
|
result = pytester.runpytest_subprocess("--color=yes")
|
||||||
result.stderr.fnmatch_lines(
|
result.stderr.fnmatch_lines(
|
||||||
"ERROR: PYTEST_THEME environment variable had an invalid value: 'invalid'. "
|
"ERROR: PYTEST_THEME environment variable has an invalid value: 'invalid'. "
|
||||||
"Only valid pygment styles are allowed."
|
"Hint: See available pygments styles with `pygmentize -L styles`."
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_code_highlight_invalid_theme_mode(
|
def test_code_highlight_invalid_theme_mode(
|
||||||
|
@ -2625,8 +2625,8 @@ class TestCodeHighlight:
|
||||||
monkeypatch.setenv("PYTEST_THEME_MODE", "invalid")
|
monkeypatch.setenv("PYTEST_THEME_MODE", "invalid")
|
||||||
result = pytester.runpytest_subprocess("--color=yes")
|
result = pytester.runpytest_subprocess("--color=yes")
|
||||||
result.stderr.fnmatch_lines(
|
result.stderr.fnmatch_lines(
|
||||||
"ERROR: PYTEST_THEME_MODE environment variable had an invalid value: 'invalid'. "
|
"ERROR: PYTEST_THEME_MODE environment variable has an invalid value: 'invalid'. "
|
||||||
"The only allowed values are 'dark' and 'light'."
|
"The allowed values are 'dark' (default) and 'light'."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue