diff --git a/beets/ui/commands/import_/display.py b/beets/ui/commands/import_/display.py index b27ed25b7..013953ca6 100644 --- a/beets/ui/commands/import_/display.py +++ b/beets/ui/commands/import_/display.py @@ -10,7 +10,7 @@ from typing_extensions import NotRequired from beets import config, ui from beets.autotag import hooks from beets.util import displayable_path -from beets.util.color import dist_colorize +from beets.util.color import colorize, dist_colorize, uncolorize from beets.util.units import human_seconds_short if TYPE_CHECKING: @@ -21,7 +21,7 @@ if TYPE_CHECKING: from beets import autotag from beets.autotag.distance import Distance from beets.library.models import Item - from beets.ui import ColorName + from beets.util.color import ColorName VARIOUS_ARTISTS = "Various Artists" @@ -47,7 +47,7 @@ class ChangeRepresentation: @cached_property def changed_prefix(self) -> str: - return ui.colorize("changed", "\u2260") + return colorize("changed", "\u2260") @cached_property def _indentation_config(self) -> confuse.Subview: @@ -120,7 +120,7 @@ class ChangeRepresentation: # Data URL. if self.match.info.data_url: - url = ui.colorize("text_faint", f"{self.match.info.data_url}") + url = colorize("text_faint", f"{self.match.info.data_url}") ui.print_(f"{self.indent_header}{url}") def show_match_details(self) -> None: @@ -216,8 +216,8 @@ class ChangeRepresentation: else: highlight_color = "text_faint" - lhs_track = ui.colorize(highlight_color, f"(#{cur_track})") - rhs_track = ui.colorize(highlight_color, f"(#{new_track})") + lhs_track = colorize(highlight_color, f"(#{cur_track})") + rhs_track = colorize(highlight_color, f"(#{new_track})") return lhs_track, rhs_track, changed @staticmethod @@ -261,8 +261,8 @@ class ChangeRepresentation: cur_length = f"({human_seconds_short(cur_length0)})" new_length = f"({human_seconds_short(new_length0)})" # colorize - lhs_length = ui.colorize(highlight_color, cur_length) - rhs_length = ui.colorize(highlight_color, new_length) + lhs_length = colorize(highlight_color, cur_length) + rhs_length = colorize(highlight_color, new_length) return lhs_length, rhs_length, changed @@ -322,7 +322,7 @@ class ChangeRepresentation: """Return the width of left or right in uncolorized characters.""" try: return len( - ui.uncolorize( + uncolorize( " ".join( [side["prefix"], side["contents"], side["suffix"]] ) @@ -411,14 +411,14 @@ class AlbumChange(ChangeRepresentation): line = f" ! {track_info.title} (#{self.format_index(track_info)})" if track_info.length: line += f" ({human_seconds_short(track_info.length)})" - ui.print_(ui.colorize("text_warning", line)) + ui.print_(colorize("text_warning", line)) if self.match.extra_items: ui.print_(f"Unmatched tracks ({len(self.match.extra_items)}):") for item in self.match.extra_items: line = f" ! {item.title} (#{self.format_index(item)})" if item.length: line += f" ({human_seconds_short(item.length)})" - ui.print_(ui.colorize("text_warning", line)) + ui.print_(colorize("text_warning", line)) class TrackChange(ChangeRepresentation): @@ -546,6 +546,6 @@ def penalty_string(distance: Distance, limit: int | None = None) -> str: penalties = [*penalties[:limit], "..."] # Prefix penalty string with U+2260: Not Equal To penalty_string = f"\u2260 {', '.join(penalties)}" - return ui.colorize("changed", penalty_string) + return colorize("changed", penalty_string) return "" diff --git a/beets/ui/commands/import_/session.py b/beets/ui/commands/import_/session.py index 1848e4192..9228ac4e2 100644 --- a/beets/ui/commands/import_/session.py +++ b/beets/ui/commands/import_/session.py @@ -4,11 +4,11 @@ from itertools import chain from beets import autotag, config, importer, logging, plugins, ui from beets.autotag import Recommendation from beets.util import PromptChoice, displayable_path +from beets.util.color import colorize, dist_colorize from beets.util.units import human_bytes, human_seconds_short from .display import ( disambig_string, - dist_colorize, penalty_string, show_change, show_item_change, @@ -30,9 +30,9 @@ class TerminalImportSession(importer.ImportSession): ui.print_() path_str0 = displayable_path(task.paths, "\n") - path_str = ui.colorize("import_path", path_str0) + path_str = colorize("import_path", path_str0) items_str0 = f"({len(task.items)} items)" - items_str = ui.colorize("import_path_items", items_str0) + items_str = colorize("import_path_items", items_str0) ui.print_(" ".join([path_str, items_str])) # Let plugins display info or prompt the user before we go through the @@ -447,7 +447,7 @@ def choose_candidate( if i == 0: metadata = dist_colorize(metadata, match.distance) else: - metadata = ui.colorize("text_highlight_minor", metadata) + metadata = colorize("text_highlight_minor", metadata) line1 = [index, distance, metadata] ui.print_(f" {' '.join(line1)}") diff --git a/beets/ui/commands/update.py b/beets/ui/commands/update.py index 9286bf12b..ddf88c325 100644 --- a/beets/ui/commands/update.py +++ b/beets/ui/commands/update.py @@ -4,6 +4,7 @@ import os from beets import library, logging, ui from beets.util import ancestry, syspath +from beets.util.color import colorize from .utils import do_query @@ -48,7 +49,7 @@ def update_items(lib, query, album, move, pretend, fields, exclude_fields=None): # Item deleted? if not item.path or not os.path.exists(syspath(item.path)): ui.print_(format(item)) - ui.print_(ui.colorize("text_error", " deleted")) + ui.print_(colorize("text_error", " deleted")) if not pretend: item.remove(True) affected_albums.add(item.album_id) diff --git a/beets/util/color.py b/beets/util/color.py index d51686aa7..2359f7d7c 100644 --- a/beets/util/color.py +++ b/beets/util/color.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Literal import confuse -from beets import config, ui +from beets import config if TYPE_CHECKING: from beets.autotag.distance import Distance @@ -164,11 +164,11 @@ def dist_colorize(string: str, dist: Distance) -> str: a distance. """ if dist <= config["match"]["strong_rec_thresh"].as_number(): - string = ui.colorize("text_success", string) + string = colorize("text_success", string) elif dist <= config["match"]["medium_rec_thresh"].as_number(): - string = ui.colorize("text_warning", string) + string = colorize("text_warning", string) else: - string = ui.colorize("text_error", string) + string = colorize("text_error", string) return string diff --git a/beetsplug/badfiles.py b/beetsplug/badfiles.py index 70509ce4f..f0ee5db7f 100644 --- a/beetsplug/badfiles.py +++ b/beetsplug/badfiles.py @@ -26,6 +26,7 @@ from beets import importer, ui from beets.plugins import BeetsPlugin from beets.ui import Subcommand from beets.util import displayable_path, par_map +from beets.util.color import colorize class CheckerCommandError(Exception): @@ -109,9 +110,7 @@ class BadFiles(BeetsPlugin): dpath = displayable_path(item.path) self._log.debug("checking path: {}", dpath) if not os.path.exists(item.path): - ui.print_( - f"{ui.colorize('text_error', dpath)}: file does not exist" - ) + ui.print_(f"{colorize('text_error', dpath)}: file does not exist") # Run the checker against the file if one is found ext = os.path.splitext(item.path)[1][1:].decode("utf8", "ignore") @@ -138,21 +137,20 @@ class BadFiles(BeetsPlugin): if status > 0: error_lines.append( - f"{ui.colorize('text_error', dpath)}: checker exited with" - f" status {status}" + f"{colorize('text_error', dpath)}: checker exited with status {status}" ) for line in output: error_lines.append(f" {line}") elif errors > 0: error_lines.append( - f"{ui.colorize('text_warning', dpath)}: checker found" + f"{colorize('text_warning', dpath)}: checker found" f" {errors} errors or warnings" ) for line in output: error_lines.append(f" {line}") elif self.verbose: - error_lines.append(f"{ui.colorize('text_success', dpath)}: ok") + error_lines.append(f"{colorize('text_success', dpath)}: ok") return error_lines @@ -173,8 +171,7 @@ class BadFiles(BeetsPlugin): def on_import_task_before_choice(self, task, session): if hasattr(task, "_badfiles_checks_failed"): ui.print_( - f"{ui.colorize('text_warning', 'BAD')} one or more files failed" - " checks:" + f"{colorize('text_warning', 'BAD')} one or more files failed checks:" ) for error in task._badfiles_checks_failed: for error_line in error: diff --git a/beetsplug/fetchart.py b/beetsplug/fetchart.py index 789182c33..1819d4531 100644 --- a/beetsplug/fetchart.py +++ b/beetsplug/fetchart.py @@ -32,6 +32,7 @@ from mediafile import image_mime_type from beets import config, importer, plugins, ui, util from beets.util import bytestring_path, get_temp_filename, sorted_walk, syspath from beets.util.artresizer import ArtResizer +from beets.util.color import colorize from beets.util.config import sanitize_pairs if TYPE_CHECKING: @@ -1596,9 +1597,7 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin): and os.path.isfile(syspath(album.artpath)) ): if not quiet: - message = ui.colorize( - "text_highlight_minor", "has album art" - ) + message = colorize("text_highlight_minor", "has album art") ui.print_(f"{album}: {message}") else: # In ordinary invocations, look for images on the @@ -1609,7 +1608,7 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin): candidate = self.art_for_album(album, local_paths) if candidate: self._set_art(album, candidate) - message = ui.colorize("text_success", "found album art") + message = colorize("text_success", "found album art") else: - message = ui.colorize("text_error", "no art found") + message = colorize("text_error", "no art found") ui.print_(f"{album}: {message}") diff --git a/beetsplug/importsource.py b/beetsplug/importsource.py index e42be3f1f..1787c6c25 100644 --- a/beetsplug/importsource.py +++ b/beetsplug/importsource.py @@ -10,8 +10,8 @@ from shutil import rmtree from beets.dbcore.query import PathQuery from beets.plugins import BeetsPlugin -from beets.ui import colorize as colorize_text from beets.ui import input_options +from beets.util.color import colorize class ImportSourcePlugin(BeetsPlugin): @@ -94,8 +94,8 @@ class ImportSourcePlugin(BeetsPlugin): # We ask the user whether they'd like to delete the item's source # directory - item_path = colorize_text("text_warning", item.filepath) - source_path = colorize_text("text_warning", srcpath) + item_path = colorize("text_warning", item.filepath) + source_path = colorize("text_warning", srcpath) print( f"The item:\n{item_path}\nis originated from:\n{source_path}\n" @@ -136,7 +136,7 @@ class ImportSourcePlugin(BeetsPlugin): print("Doing so will delete the following items' sources as well:") for searched_item in item._db.items(source_dir_query): - print(colorize_text("text_warning", searched_item.filepath)) + print(colorize("text_warning", searched_item.filepath)) print("Would you like to continue?") continue_resp = input_options( diff --git a/beetsplug/play.py b/beetsplug/play.py index 0d96ee97f..2474f3908 100644 --- a/beetsplug/play.py +++ b/beetsplug/play.py @@ -22,6 +22,7 @@ from beets import config, ui, util from beets.plugins import BeetsPlugin from beets.ui import Subcommand from beets.util import PromptChoice, get_temp_filename +from beets.util.color import colorize # Indicate where arguments should be inserted into the command string. # If this is missing, they're placed at the end. @@ -132,7 +133,7 @@ class PlayPlugin(BeetsPlugin): paths = [relpath(path, relative_to) for path in paths] if not selection: - ui.print_(ui.colorize("text_warning", f"No {item_type} to play.")) + ui.print_(colorize("text_warning", f"No {item_type} to play.")) return open_args = self._playlist_or_paths(paths) @@ -197,7 +198,7 @@ class PlayPlugin(BeetsPlugin): item_type += "s" ui.print_( - ui.colorize( + colorize( "text_warning", f"You are about to queue {len(selection)} {item_type}.", ) diff --git a/test/ui/test_field_diff.py b/test/ui/test_field_diff.py index 24bac0123..80ee94a45 100644 --- a/test/ui/test_field_diff.py +++ b/test/ui/test_field_diff.py @@ -17,7 +17,7 @@ class TestFieldDiff: def patch_colorize(self, monkeypatch): """Patch to return a deterministic string format instead of ANSI codes.""" monkeypatch.setattr( - "beets.ui._colorize", + "beets.util.color._colorize", lambda color_name, text: f"[{color_name}]{text}[/]", ) diff --git a/test/util/test_color.py b/test/util/test_color.py index 931540e5f..98aa994c6 100644 --- a/test/util/test_color.py +++ b/test/util/test_color.py @@ -1,24 +1,24 @@ from unittest import TestCase -from beets import ui +from beets.util.color import color_split, uncolorize class ColorTestCase(TestCase): - def test_colorize(self): - assert "test" == ui.uncolorize("test") - txt = ui.uncolorize("\x1b[31mtest\x1b[39;49;00m") + def test_uncolorize(self): + assert "test" == uncolorize("test") + txt = uncolorize("\x1b[31mtest\x1b[39;49;00m") assert "test" == txt - txt = ui.uncolorize("\x1b[31mtest\x1b[39;49;00m test") + txt = uncolorize("\x1b[31mtest\x1b[39;49;00m test") assert "test test" == txt - txt = ui.uncolorize("\x1b[31mtest\x1b[39;49;00mtest") + txt = uncolorize("\x1b[31mtest\x1b[39;49;00mtest") assert "testtest" == txt - txt = ui.uncolorize("test \x1b[31mtest\x1b[39;49;00m test") + txt = uncolorize("test \x1b[31mtest\x1b[39;49;00m test") assert "test test test" == txt def test_color_split(self): exp = ("test", "") - res = ui.color_split("test", 5) + res = color_split("test", 5) assert exp == res exp = ("\x1b[31mtes\x1b[39;49;00m", "\x1b[31mt\x1b[39;49;00m") - res = ui.color_split("\x1b[31mtest\x1b[39;49;00m", 3) + res = color_split("\x1b[31mtest\x1b[39;49;00m", 3) assert exp == res