Skip to content

Commit

Permalink
fix: improve Black import error [optional-black-improvement]
Browse files Browse the repository at this point in the history
  • Loading branch information
akaihola committed Jan 2, 2025
1 parent ee0de17 commit 95605ca
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 28 deletions.
36 changes: 14 additions & 22 deletions src/darker/formatters/black_formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
import logging
from typing import TYPE_CHECKING, TypedDict

from darker.exceptions import DependencyError
from darker.files import find_pyproject_toml
from darker.formatters.base_formatter import BaseFormatter
from darkgraylib.config import ConfigurationError
Expand Down Expand Up @@ -93,24 +92,11 @@ def read_config(self, src: tuple[str, ...], args: Namespace) -> None:
def _read_config_file(self, config_path: str) -> None: # noqa: C901
# Local import so Darker can be run without Black installed.
# Do error handling here. This is the first Black importing method being hit.
try:
from black import ( # pylint: disable=import-outside-toplevel
parse_pyproject_toml,
re_compile_maybe_verbose,
)
except ImportError as exc:
logger.warning(
"To re-format code using Black, install it using e.g."
" `pip install 'darker[black]'` or"
" `pip install black`"
)
logger.warning(
"To use a different formatter or no formatter, select it on the"
" command line (e.g. `--formatter=none`) or configuration"
" (e.g. `formatter=none`)"
)
message = "Can't find the Black package"
raise DependencyError(message) from exc
# pylint: disable=import-outside-toplevel
from darker.formatters.black_wrapper import (
parse_pyproject_toml,
re_compile_maybe_verbose,
)

raw_config = parse_pyproject_toml(config_path)
if "line_length" in raw_config:
Expand Down Expand Up @@ -171,7 +157,10 @@ def run(self, content: TextDocument) -> TextDocument:
"""
# Local import so Darker can be run without Black installed.
# No need for error handling, already done in `BlackFormatter.read_config`.
from black import format_str # pylint: disable=import-outside-toplevel
# pylint: disable=import-outside-toplevel
from darker.formatters.black_wrapper import (
format_str,
)

contents_for_black = content.string_with_newline("\n")
if contents_for_black.strip():
Expand All @@ -196,8 +185,11 @@ def _make_black_options(self) -> Mode:

# Local import so Darker can be run without Black installed.
# No need for error handling, already done in `BlackFormatter.read_config`.
from black import FileMode as Mode # pylint: disable=import-outside-toplevel
from black import TargetVersion # pylint: disable=import-outside-toplevel
# pylint: disable=import-outside-toplevel
from darker.formatters.black_wrapper import (
FileMode as Mode,
TargetVersion,
)

mode = BlackModeAttributes()
if "line_length" in self.config:
Expand Down
40 changes: 40 additions & 0 deletions src/darker/formatters/black_wrapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Attempt to import Black internals needed by the Black formatter plugin."""

import logging

from darker.exceptions import DependencyError

logger = logging.getLogger(__name__)

try:
import black # noqa: F401 # pylint: disable=unused-import
except ImportError as exc:
logger.warning(
"To re-format code using Black, install it using e.g."
" `pip install 'darker[black]'` or"
" `pip install black`"
)
logger.warning(
"To use a different formatter or no formatter, select it on the"
" command line (e.g. `--formatter=none`) or configuration"
" (e.g. `formatter=none`)"
)
MESSAGE = "Can't find the Black package"
raise DependencyError(MESSAGE) from exc

from black import ( # noqa: E402 # pylint: disable=unused-import,wrong-import-position
FileMode,
TargetVersion,
format_str,
parse_pyproject_toml,
re_compile_maybe_verbose,
)


__all__ = [
"FileMode",
"TargetVersion",
"format_str",

Check failure on line 37 in src/darker/formatters/black_wrapper.py

View workflow job for this annotation

GitHub Actions / isort

src/darker/formatters/black_wrapper.py#L30-L37

re_compile_maybe_verbose, ) - __all__ = [ "FileMode", "TargetVersion",
"parse_pyproject_toml",
"re_compile_maybe_verbose",
]
1 change: 1 addition & 0 deletions src/darker/tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def _package_present(
def black_present(*, present: bool) -> Generator[None, None, None]:
"""Context manager to remove or add the ``black`` package temporarily for a test."""
with _package_present("black", present):
del sys.modules["darker.formatters.black_wrapper"]
yield


Expand Down
6 changes: 4 additions & 2 deletions src/darker/tests/test_command_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,9 @@ def test_black_options(black_options_files, options, expect):
# shared by all test cases. The "main.py" file modified by the test run needs to be
# reset to its original content before the next test case.
black_options_files["main.py"].write_bytes(b'print ("Hello World!")\n')
with patch("black.FileMode", wraps=FileMode) as file_mode_class:
with patch(
"darker.formatters.black_wrapper.FileMode", wraps=FileMode
) as file_mode_class:
# end of test setup, now call the function under test

main(options + [str(path) for path in black_options_files.values()])
Expand Down Expand Up @@ -718,7 +720,7 @@ def test_black_config_file_and_options(
mode_class_mock = Mock(wraps=FileMode)
# Speed up tests by mocking `format_str` to skip running Black
format_str = Mock(return_value="a = [1, 2,]")
with patch("black.FileMode", mode_class_mock), patch(
with patch("darker.formatters.black_wrapper.FileMode", mode_class_mock), patch(
"black.format_str", format_str
):
# end of test setup, now call the function under test
Expand Down
8 changes: 4 additions & 4 deletions src/darker/tests/test_formatters_black.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ def test_run(encoding, newline):
def test_run_always_uses_unix_newlines(newline):
"""Content is always passed to Black with Unix newlines"""
src = TextDocument.from_str(f"print ( 'touché' ){newline}")
with patch("black.format_str") as format_str:
with patch("darker.formatters.black_wrapper.format_str") as format_str:
format_str.return_value = 'print("touché")\n'

_ = BlackFormatter().run(src)
Expand Down Expand Up @@ -390,9 +390,9 @@ def test_run_configuration(
):
"""`BlackFormatter.run` passes correct configuration to Black."""
src = TextDocument.from_str("import os\n")
with patch("black.format_str") as format_str, raises_or_matches(
expect, []
) as check:
with patch(
"darker.formatters.black_wrapper.format_str"
) as format_str, raises_or_matches(expect, []) as check:
format_str.return_value = "import os\n"
formatter = BlackFormatter()
formatter.config = black_config
Expand Down

0 comments on commit 95605ca

Please sign in to comment.