Skip to content

Commit

Permalink
improved the terminal formatting
Browse files Browse the repository at this point in the history
added in the ability to use pygments TerminalTrueColorFormatter and Terminal256Formatter.

I also created a default format which is similar to the default format in TerminalFormatter.
  • Loading branch information
FamousPond committed May 10, 2024
1 parent 5af46f3 commit 0f1c424
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 6 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ Jake VanderPlas
Jakob van Santen
Jakub Mitoraj
James Bourbeau
James Hutchins
Jan Balster
Janne Vanhala
Jason R. Coombs
Expand Down
7 changes: 7 additions & 0 deletions changelog/11666.improvement.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Improved the terminal writer to be able to use pygments Terminal256Formatter and TerminalTrueColorFormatter.

These can be set by the user with the environmental variable PYTEST_THEME.

There is also a default format set with both a light and dark mode which can be changed with the environmental variable PYTEST_THEME_MODE.

If no environmental variables are set it defaults to dark mode.
161 changes: 155 additions & 6 deletions src/_pytest/_io/terminalwriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from typing import Sequence
from typing import TextIO

from pygments.style import Style

from ..compat import assert_never
from .wcwidth import wcswidth

Expand Down Expand Up @@ -218,14 +220,75 @@ def _highlight(
return source
else:
try:
highlighted: str = highlight(
source,
Lexer(),
TerminalFormatter(
# Establishes the style to be used.
if os.environ.get("COLORTERM", "") not in (
"truecolor",
"24bit",
) and "256" not in os.environ.get("TERM", ""):
# The default case

PytestTerminalFormat = TerminalFormatter(
bg=os.getenv("PYTEST_THEME_MODE", "dark"),
style=os.getenv("PYTEST_THEME"),
),
)
)

else:
if os.getenv("PYTEST_THEME") is None:
if os.getenv("PYTEST_THEME_MODE") is None:
# Neither PYTEST_THEME nor PYTEST_THEME_MODE have been set so using dark mode
SelectedStyle = DarkModeStyle

Check warning on line 239 in src/_pytest/_io/terminalwriter.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/_io/terminalwriter.py#L239

Added line #L239 was not covered by tests

elif os.getenv("PYTEST_THEME_MODE") == "light":
# PYTEST_THEME has not been set but PYTEST_THEME_MODE has been set to light mode
SelectedStyle = LightModeStyle

Check warning on line 243 in src/_pytest/_io/terminalwriter.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/_io/terminalwriter.py#L243

Added line #L243 was not covered by tests

else:
# PYTEST_THEME has not been set and PYTEST_THEME_MODE is not light so use dark
SelectedStyle = DarkModeStyle

Check warning on line 247 in src/_pytest/_io/terminalwriter.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/_io/terminalwriter.py#L247

Added line #L247 was not covered by tests

else:
# PYTEST_THEME has been set so use it
SelectedStyle = None

Check warning on line 251 in src/_pytest/_io/terminalwriter.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/_io/terminalwriter.py#L251

Added line #L251 was not covered by tests

# The style has now been selected the right formatter needs to be used
if os.environ.get("COLORTERM", "") in ("truecolor", "24bit"):
# The true color formatter
from pygments.formatters.terminal256 import (

Check warning on line 256 in src/_pytest/_io/terminalwriter.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/_io/terminalwriter.py#L256

Added line #L256 was not covered by tests
TerminalTrueColorFormatter,
)

# If the style is user input
if SelectedStyle is None:
PytestTerminalFormat = TerminalTrueColorFormatter(

Check warning on line 262 in src/_pytest/_io/terminalwriter.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/_io/terminalwriter.py#L262

Added line #L262 was not covered by tests
style=os.getenv("PYTEST_THEME")
)
else:
PytestTerminalFormat = TerminalTrueColorFormatter(

Check warning on line 266 in src/_pytest/_io/terminalwriter.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/_io/terminalwriter.py#L266

Added line #L266 was not covered by tests
style=SelectedStyle
)

elif "256" in os.environ.get("TERM", ""):
# The 256 color formater
from pygments.formatters.terminal256 import Terminal256Formatter

Check warning on line 272 in src/_pytest/_io/terminalwriter.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/_io/terminalwriter.py#L272

Added line #L272 was not covered by tests

# If the style is user input
if SelectedStyle is None:
PytestTerminalFormat = Terminal256Formatter(

Check warning on line 276 in src/_pytest/_io/terminalwriter.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/_io/terminalwriter.py#L276

Added line #L276 was not covered by tests
style=os.getenv("PYTEST_THEME")
)
else:
PytestTerminalFormat = Terminal256Formatter(

Check warning on line 280 in src/_pytest/_io/terminalwriter.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/_io/terminalwriter.py#L280

Added line #L280 was not covered by tests
style=SelectedStyle
)

else:
# The default case (Although this code should not be reached)
PytestTerminalFormat = TerminalFormatter(

Check warning on line 286 in src/_pytest/_io/terminalwriter.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/_io/terminalwriter.py#L286

Added line #L286 was not covered by tests
bg=os.getenv("PYTEST_THEME_MODE", "dark"),
style=os.getenv("PYTEST_THEME"),
)

highlighted: str = highlight(source, Lexer(), PytestTerminalFormat)
# 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":
Expand All @@ -249,3 +312,89 @@ def _highlight(
os.getenv("PYTEST_THEME_MODE")
)
) from e


class DarkModeStyle(Style):
# The default dark mode style class from TerminalFormatter recreated to work with
# both TerminalTrueColorFormatter and Terminal256Formatter

from pygments.token import Comment
from pygments.token import Error
from pygments.token import Generic
from pygments.token import Keyword
from pygments.token import Name
from pygments.token import Number
from pygments.token import Operator
from pygments.token import String
from pygments.token import Token
from pygments.token import Whitespace

styles = {
Token: "",
Whitespace: "ansibrightblack",
Comment: "ansibrightblack",
Comment.Preproc: "ansibrightcyan",
Keyword: "ansibrightblue",
Keyword.Type: "ansibrightcyan",
Operator.Word: "ansibrightmagenta",
Name.Builtin: "ansibrightcyan",
Name.Function: "ansibrightgreen",
Name.Namespace: "ansibrightcyan",
Name.Class: "ansibrightgreen",
Name.Exception: "ansibrightcyan",
Name.Decorator: "ansigray",
Name.Variable: "ansibrightred",
Name.Constant: "ansibrightred",
Name.Attribute: "ansibrightcyan",
Name.Tag: "ansibrightblue",
String: "ansiyellow",
Number: "ansibrightblue",
Generic.Deleted: "ansibrightred",
Generic.Inserted: "ansigreen",
Generic.Subheading: "ansimagenta",
Generic.Error: "ansibrightred",
Error: "ansibrightred",
}


class LightModeStyle(Style):
# The default light mode style class from TerminalFormatter recreated to work with
# both TerminalTrueColorFormatter and Terminal256Formatter

from pygments.token import Comment
from pygments.token import Error
from pygments.token import Generic
from pygments.token import Keyword
from pygments.token import Name
from pygments.token import Number
from pygments.token import Operator
from pygments.token import String
from pygments.token import Token
from pygments.token import Whitespace

styles = {
Token: "",
Whitespace: "ansigray",
Comment: "ansigray",
Comment.Preproc: "ansicyan",
Keyword: "ansiblue",
Keyword.Type: "ansicyan",
Operator.Word: "ansimagenta",
Name.Builtin: "ansicyan",
Name.Function: "ansigreen",
Name.Namespace: "ansicyan",
Name.Class: "ansigreen",
Name.Exception: "ansicyan",
Name.Decorator: "ansibrightblack",
Name.Variable: "ansired",
Name.Constant: "ansired",
Name.Attribute: "ansicyan",
Name.Tag: "ansibrightblue",
String: "ansiyellow",
Number: "ansiblue",
Generic.Deleted: "ansibrightred",
Generic.Inserted: "ansigreen",
Generic.Subheading: "ansimagenta",
Generic.Error: "ansibrightred",
Error: "ansibrightred",
}

0 comments on commit 0f1c424

Please sign in to comment.