terminalwriter: factor out pygments lexer & formatter selection to own functions
We intend to add some more logic here.
This commit is contained in:
		
							parent
							
								
									3d91e42229
								
							
						
					
					
						commit
						67a570aea9
					
				| 
						 | 
				
			
			@ -8,11 +8,17 @@ from typing import Literal
 | 
			
		|||
from typing import Optional
 | 
			
		||||
from typing import Sequence
 | 
			
		||||
from typing import TextIO
 | 
			
		||||
from typing import TYPE_CHECKING
 | 
			
		||||
 | 
			
		||||
from ..compat import assert_never
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -194,58 +200,76 @@ class TerminalWriter:
 | 
			
		|||
        for indent, new_line in zip(indents, new_lines):
 | 
			
		||||
            self.line(indent + new_line)
 | 
			
		||||
 | 
			
		||||
    def _highlight(
 | 
			
		||||
        self, source: str, lexer: Literal["diff", "python"] = "python"
 | 
			
		||||
    ) -> str:
 | 
			
		||||
        """Highlight the given source if we have markup support."""
 | 
			
		||||
    def _get_pygments_lexer(
 | 
			
		||||
        self, lexer: Literal["python", "diff"]
 | 
			
		||||
    ) -> Optional["Lexer"]:
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
        if not source or not self.hasmarkup or not self.code_highlight:
 | 
			
		||||
            return source
 | 
			
		||||
        theme = os.getenv("PYTEST_THEME")
 | 
			
		||||
        theme_mode = os.getenv("PYTEST_THEME_MODE", "dark")
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            from pygments.formatters.terminal import TerminalFormatter
 | 
			
		||||
 | 
			
		||||
            if lexer == "python":
 | 
			
		||||
                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]
 | 
			
		||||
            return TerminalFormatter(bg=theme_mode, style=theme)
 | 
			
		||||
 | 
			
		||||
                # 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.
 | 
			
		||||
                return "\x1b[0m" + highlighted
 | 
			
		||||
            except pygments.util.ClassNotFound as e:
 | 
			
		||||
                raise UsageError(
 | 
			
		||||
                    "PYTEST_THEME environment variable had an invalid value: '{}'. "
 | 
			
		||||
                    "Only valid pygment styles are allowed.".format(
 | 
			
		||||
                        os.getenv("PYTEST_THEME")
 | 
			
		||||
                    )
 | 
			
		||||
                ) from e
 | 
			
		||||
            except pygments.util.OptionError as e:
 | 
			
		||||
                raise UsageError(
 | 
			
		||||
                    "PYTEST_THEME_MODE environment variable had an invalid value: '{}'. "
 | 
			
		||||
                    "The only allowed values are 'dark' and 'light'.".format(
 | 
			
		||||
                        os.getenv("PYTEST_THEME_MODE")
 | 
			
		||||
                    )
 | 
			
		||||
                ) from e
 | 
			
		||||
        except pygments.util.ClassNotFound as e:
 | 
			
		||||
            raise UsageError(
 | 
			
		||||
                f"PYTEST_THEME environment variable had an invalid value: '{theme}'. "
 | 
			
		||||
                "Only valid pygment styles are allowed."
 | 
			
		||||
            ) from e
 | 
			
		||||
        except pygments.util.OptionError as e:
 | 
			
		||||
            raise UsageError(
 | 
			
		||||
                f"PYTEST_THEME_MODE environment variable had an invalid value: '{theme_mode}'. "
 | 
			
		||||
                "The only allowed values are 'dark' and 'light'."
 | 
			
		||||
            ) from e
 | 
			
		||||
 | 
			
		||||
    def _highlight(
 | 
			
		||||
        self, source: str, lexer: Literal["diff", "python"] = "python"
 | 
			
		||||
    ) -> str:
 | 
			
		||||
        """Highlight the given source if we have markup support."""
 | 
			
		||||
        if not source or not self.hasmarkup or not self.code_highlight:
 | 
			
		||||
            return source
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue