diff --git a/src/darker/__main__.py b/src/darker/__main__.py index a5ab1a458..0ead62eb3 100644 --- a/src/darker/__main__.py +++ b/src/darker/__main__.py @@ -258,10 +258,11 @@ def print_source(new: TextDocument) -> None: """Print the reformatted Python source code""" if sys.stdout.isatty(): try: - # pylint: disable=import-outside-toplevel - from pygments import highlight - from pygments.formatters import TerminalFormatter - from pygments.lexers.python import PythonLexer + ( + highlight, + TerminalFormatter, # pylint: disable=invalid-name + PythonLexer, + ) = _import_pygments() # type: ignore except ImportError: print(new.string, end="") else: @@ -270,6 +271,21 @@ def print_source(new: TextDocument) -> None: print(new.string, end="") +def _import_pygments(): # type: ignore + """Import within a function to ease mocking the import in unit-tests. + + Cannot be typed as it imports parts of its own return type. + """ + # pylint: disable=import-outside-toplevel + from pygments import highlight + from pygments.formatters import ( # pylint: disable=no-name-in-module + TerminalFormatter, + ) + from pygments.lexers.python import PythonLexer + + return highlight, TerminalFormatter, PythonLexer + + def main(argv: List[str] = None) -> int: """Parse the command line and reformat and optionally lint each source file diff --git a/src/darker/tests/test_main.py b/src/darker/tests/test_main.py index b352e3f28..104081e62 100644 --- a/src/darker/tests/test_main.py +++ b/src/darker/tests/test_main.py @@ -8,7 +8,7 @@ from pathlib import Path from textwrap import dedent from types import SimpleNamespace -from unittest.mock import call, patch +from unittest.mock import Mock, call, patch import pytest @@ -778,6 +778,50 @@ def test_modify_file(tmp_path, new_content, expect): assert result == expect +@pytest.mark.kwparametrize( + dict( + new_content=TextDocument(lines=['print("foo")']), + tty=False, + with_pygments=False, + expect=('print("foo")\n',), + ), + dict( + new_content=TextDocument(lines=['print("foo")']), + tty=False, + with_pygments=True, + expect=('print("foo")\n',), + ), + dict( + new_content=TextDocument(lines=['print("foo")']), + tty=True, + with_pygments=False, + expect=('print("foo")\n',), + ), + dict( + new_content=TextDocument(lines=['print("foo")']), + tty=True, + with_pygments=True, + expect=( + '\x1b[36mprint\x1b[39;49;00m(\x1b[33m"\x1b[39;49;00mfoo' + + '\x1b[33m"\x1b[39;49;00m)\n', + '\x1b[36mprint\x1b[39;49;00m(\x1b[33m"\x1b[39;49;00m\x1b[33mfoo' + + '\x1b[39;49;00m\x1b[33m"\x1b[39;49;00m)\n', + ), + ), +) +def test_print_source(new_content, tty, with_pygments, expect, capsys): + """Highlight is applied only if tty, final newline is handled correctly.""" + with patch("sys.stdout.isatty", Mock(return_value=tty)), patch( + "darker.__main__._import_pygments", + Mock( + return_value=darker.__main__._import_pygments(), + side_effect=None if with_pygments else ImportError(), + ), + ): + darker.__main__.print_source(new_content) + assert capsys.readouterr().out in expect + + def test_stdout_path_resolution(git_repo, capsys): """When using ``--stdout``, file paths are resolved correctly""" git_repo.add({"src/menu.py": "print ( 'foo' )\n"})