From 7820bd6aba147bd85424676a59e9b14c50473bb4 Mon Sep 17 00:00:00 2001 From: Antti Kaihola <13725+akaihola@users.noreply.github.com> Date: Mon, 28 Oct 2024 22:09:20 +0200 Subject: [PATCH] feat: verbose error if Black not found --- src/darker/formatters/black_formatter.py | 34 +++++++++++++++++++----- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/darker/formatters/black_formatter.py b/src/darker/formatters/black_formatter.py index a4af39eb1..6bcdd13e4 100644 --- a/src/darker/formatters/black_formatter.py +++ b/src/darker/formatters/black_formatter.py @@ -37,10 +37,13 @@ from __future__ import annotations import logging +import sys 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.command_line import EXIT_CODE_DEPENDENCY from darkgraylib.config import ConfigurationError from darkgraylib.utils import TextDocument @@ -90,11 +93,26 @@ def read_config(self, src: tuple[str, ...], args: Namespace) -> None: self._read_cli_args(args) def _read_config_file(self, config_path: str) -> None: # noqa: C901 - # Local import so Darker can be run without Black installed - from black import ( # pylint: disable=import-outside-toplevel - parse_pyproject_toml, - re_compile_maybe_verbose, - ) + # 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 raw_config = parse_pyproject_toml(config_path) if "line_length" in raw_config: @@ -153,7 +171,8 @@ def run(self, content: TextDocument) -> TextDocument: :return: The reformatted content """ - # Local import so Darker can be run without Black installed + # 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 contents_for_black = content.string_with_newline("\n") @@ -177,7 +196,8 @@ def _make_black_options(self) -> Mode: # pass them to Black's ``format_str()``. File exclusion options aren't needed # since at this point we already have a single file's content to work on. - # Local import so Darker can be run without Black installed + # 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