From b3d434f58f449050d54aa2042c53210bc1318eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Nejus?= Date: Sat, 30 Aug 2025 18:33:25 +0100 Subject: [PATCH] Delegate attribute access to logging --- beets/art.py | 6 +++--- beets/autotag/match.py | 2 +- beets/importer/stages.py | 5 +---- beets/importer/tasks.py | 41 ++++++++++++++++-------------------- beets/library/models.py | 2 +- beets/ui/commands.py | 24 ++++++++++----------- beets/util/__init__.py | 2 +- beets/util/artresizer.py | 8 +++---- beetsplug/acousticbrainz.py | 2 +- beetsplug/badfiles.py | 7 +++---- beetsplug/bpd/__init__.py | 4 ++-- beetsplug/bpsync.py | 16 +++++++------- beetsplug/chroma.py | 10 ++++----- beetsplug/convert.py | 29 +++++++++++++------------ beetsplug/deezer.py | 8 +++---- beetsplug/duplicates.py | 28 ++++++++++++------------- beetsplug/fetchart.py | 42 ++++++++++++++++++------------------- beetsplug/ftintitle.py | 8 +++---- beetsplug/hook.py | 2 +- beetsplug/importadded.py | 23 +++++++++----------- beetsplug/ipfs.py | 2 +- beetsplug/keyfinder.py | 4 ++-- beetsplug/lastimport.py | 7 +++---- beetsplug/lyrics.py | 17 ++++++++------- beetsplug/missing.py | 6 +++--- beetsplug/mpdstats.py | 8 +++---- beetsplug/mpdupdate.py | 4 ++-- beetsplug/parentwork.py | 3 +-- beetsplug/playlist.py | 2 +- beetsplug/replaygain.py | 25 ++++++++++------------ beetsplug/scrub.py | 4 ++-- beetsplug/spotify.py | 24 ++++++++++----------- beetsplug/subsonicupdate.py | 2 +- beetsplug/thumbnails.py | 2 +- pyproject.toml | 1 + 35 files changed, 179 insertions(+), 201 deletions(-) diff --git a/beets/art.py b/beets/art.py index 73e875202..656c303ce 100644 --- a/beets/art.py +++ b/beets/art.py @@ -38,7 +38,7 @@ def get_art(log, item): try: mf = mediafile.MediaFile(syspath(item.path)) except mediafile.UnreadableFileError as exc: - log.warning("Could not extract art from {}: {}", item.filepath, exc) + log.warning("Could not extract art from {.filepath}: {}", item, exc) return return mf.art @@ -88,7 +88,7 @@ def embed_item( # Make sure the image kind is safe (some formats only support PNG # and JPEG). if image.mime_type not in ("image/jpeg", "image/png"): - log.info("not embedding image of unsupported type: {}", image.mime_type) + log.info("not embedding image of unsupported type: {.mime_type}", image) return item.try_write(path=itempath, tags={"images": [image]}, id3v23=id3v23) @@ -185,7 +185,7 @@ def extract(log, outpath, item): # Add an extension to the filename. ext = mediafile.image_extension(art) if not ext: - log.warning("Unknown image type in {}.", item.filepath) + log.warning("Unknown image type in {.filepath}.", item) return outpath += bytestring_path(f".{ext}") diff --git a/beets/autotag/match.py b/beets/autotag/match.py index dd992facc..8fec844a6 100644 --- a/beets/autotag/match.py +++ b/beets/autotag/match.py @@ -197,7 +197,7 @@ def _add_candidate( checking the track count, ordering the items, checking for duplicates, and calculating the distance. """ - log.debug("Candidate: {} - {} ({})", info.artist, info.album, info.album_id) + log.debug("Candidate: {0.artist} - {0.album} ({0.album_id})", info) # Discard albums with zero tracks. if not info.tracks: diff --git a/beets/importer/stages.py b/beets/importer/stages.py index eb7b22ccf..d99b742a2 100644 --- a/beets/importer/stages.py +++ b/beets/importer/stages.py @@ -82,10 +82,7 @@ def query_tasks(session: ImportSession): # Search for albums. for album in session.lib.albums(session.query): log.debug( - "yielding album {}: {} - {}", - album.id, - album.albumartist, - album.album, + "yielding album {0.id}: {0.albumartist} - {0.album}", album ) items = list(album.items()) _freshen_items(items) diff --git a/beets/importer/tasks.py b/beets/importer/tasks.py index 7fa11c972..b4d566032 100644 --- a/beets/importer/tasks.py +++ b/beets/importer/tasks.py @@ -271,7 +271,7 @@ class ImportTask(BaseImportTask): for item in duplicate_items: item.remove() if lib.directory in util.ancestry(item.path): - log.debug("deleting duplicate {}", item.filepath) + log.debug("deleting duplicate {.filepath}", item) util.remove(item.path) util.prune_dirs(os.path.dirname(item.path), lib.directory) @@ -552,12 +552,11 @@ class ImportTask(BaseImportTask): ] if overwritten_fields: log.debug( - "Reimported {} {}. Not preserving flexible attributes {}. " - "Path: {}", + "Reimported {0} {1.id}. Not preserving flexible attributes {2}. " + "Path: {1.filepath}", noun, - new_obj.id, + new_obj, overwritten_fields, - new_obj.filepath, ) for key in overwritten_fields: del existing_fields[key] @@ -576,17 +575,15 @@ class ImportTask(BaseImportTask): self.album.artpath = replaced_album.artpath self.album.store() log.debug( - "Reimported album {}. Preserving attribute ['added']. " - "Path: {}", - self.album.id, - self.album.filepath, + "Reimported album {0.album.id}. Preserving attribute ['added']. " + "Path: {0.album.filepath}", + self, ) log.debug( - "Reimported album {}. Preserving flexible attributes {}. " - "Path: {}", - self.album.id, + "Reimported album {0.album.id}. Preserving flexible" + " attributes {1}. Path: {0.album.filepath}", + self, list(album_fields.keys()), - self.album.filepath, ) for item in self.imported_items(): @@ -595,21 +592,19 @@ class ImportTask(BaseImportTask): if dup_item.added and dup_item.added != item.added: item.added = dup_item.added log.debug( - "Reimported item {}. Preserving attribute ['added']. " - "Path: {}", - item.id, - item.filepath, + "Reimported item {0.id}. Preserving attribute ['added']. " + "Path: {0.filepath}", + item, ) item_fields = _reduce_and_log( item, dup_item._values_flex, REIMPORT_FRESH_FIELDS_ITEM ) item.update(item_fields) log.debug( - "Reimported item {}. Preserving flexible attributes {}. " - "Path: {}", - item.id, + "Reimported item {0.id}. Preserving flexible attributes {1}. " + "Path: {0.filepath}", + item, list(item_fields.keys()), - item.filepath, ) item.store() @@ -619,7 +614,7 @@ class ImportTask(BaseImportTask): """ for item in self.imported_items(): for dup_item in self.replaced_items[item]: - log.debug("Replacing item {}: {}", dup_item.id, item.filepath) + log.debug("Replacing item {.id}: {.filepath}", dup_item, item) dup_item.remove() log.debug( "{} of {} items replaced", @@ -1067,7 +1062,7 @@ class ImportTaskFactory: # Now read albums from the extracted directory. self.toppath = archive_task.toppath - log.debug("Archive extracted to: {}", self.toppath) + log.debug("Archive extracted to: {.toppath}", self) return archive_task def read_item(self, path: util.PathBytes): diff --git a/beets/library/models.py b/beets/library/models.py index cd7bc1e92..cbee2a411 100644 --- a/beets/library/models.py +++ b/beets/library/models.py @@ -1012,7 +1012,7 @@ class Item(LibModel): if move: # Check whether this file is inside the library directory. if self._db and self._db.directory in util.ancestry(self.path): - log.debug("moving {} to synchronize path", self.filepath) + log.debug("moving {.filepath} to synchronize path", self) self.move(with_album=with_album) self.store() diff --git a/beets/ui/commands.py b/beets/ui/commands.py index 1c92186d2..911a5cfd3 100755 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -1280,11 +1280,10 @@ class TerminalImportSession(importer.ImportSession): dup_choices = [c for c in all_choices if c.short == short] for c in dup_choices[1:]: log.warning( - "Prompt choice '{}' removed due to conflict " - "with '{}' (short letter: '{}')", - c.long, - dup_choices[0].long, - c.short, + "Prompt choice '{0.long}' removed due to conflict " + "with '{1[0].long}' (short letter: '{0.short}')", + c, + dup_choices, ) extra_choices.remove(c) @@ -1639,9 +1638,8 @@ def update_items(lib, query, album, move, pretend, fields, exclude_fields=None): # Did the item change since last checked? if item.current_mtime() <= item.mtime: log.debug( - "skipping {} because mtime is up to date ({})", - item.filepath, - item.mtime, + "skipping {0.filepath} because mtime is up to date ({0.mtime})", + item, ) continue @@ -1649,7 +1647,7 @@ def update_items(lib, query, album, move, pretend, fields, exclude_fields=None): try: item.read() except library.ReadError as exc: - log.error("error reading {}: {}", item.filepath, exc) + log.error("error reading {.filepath}: {}", item, exc) continue # Special-case album artist when it matches track artist. (Hacky @@ -1882,7 +1880,7 @@ def show_stats(lib, query, exact): try: total_size += os.path.getsize(syspath(item.path)) except OSError as exc: - log.info("could not get size of {}: {}", item.path, exc) + log.info("could not get size of {.path}: {}", item, exc) else: total_size += int(item.length * item.bitrate / 8) total_time += item.length @@ -2173,7 +2171,7 @@ def move_items( ) for obj in objs: - log.debug("moving: {}", obj.filepath) + log.debug("moving: {.filepath}", obj) if export: # Copy without affecting the database. @@ -2256,14 +2254,14 @@ def write_items(lib, query, pretend, force): for item in items: # Item deleted? if not os.path.exists(syspath(item.path)): - log.info("missing file: {}", item.filepath) + log.info("missing file: {.filepath}", item) continue # Get an Item object reflecting the "clean" (on-disk) state. try: clean_item = library.Item.from_path(item.path) except library.ReadError as exc: - log.error("error reading {}: {}", item.filepath, exc) + log.error("error reading {.filepath}: {}", item, exc) continue # Check for and display changes. diff --git a/beets/util/__init__.py b/beets/util/__init__.py index 3ac7ad6a9..f895a60ee 100644 --- a/beets/util/__init__.py +++ b/beets/util/__init__.py @@ -126,7 +126,7 @@ class HumanReadableError(Exception): """ if self.tb: logger.debug(self.tb) - logger.error("{}: {}", self.error_kind, self.args[0]) + logger.error("{0.error_kind}: {0.args[0]}", self) class FilesystemError(HumanReadableError): diff --git a/beets/util/artresizer.py b/beets/util/artresizer.py index 1e3b836ae..5ecde5140 100644 --- a/beets/util/artresizer.py +++ b/beets/util/artresizer.py @@ -306,9 +306,9 @@ class IMBackend(LocalBackend): except subprocess.CalledProcessError as exc: log.warning("ImageMagick size query failed") log.debug( - "`convert` exited with (status {}) when " + "`convert` exited with (status {.returncode}) when " "getting size with command {}:\n{}", - exc.returncode, + exc, cmd, exc.output.strip(), ) @@ -441,8 +441,8 @@ class IMBackend(LocalBackend): convert_proc.wait() if convert_proc.returncode: log.debug( - "ImageMagick convert failed with status {}: {!r}", - convert_proc.returncode, + "ImageMagick convert failed with status {.returncode}: {!r}", + convert_proc, convert_stderr, ) return None diff --git a/beetsplug/acousticbrainz.py b/beetsplug/acousticbrainz.py index 29c51d302..92a1976a1 100644 --- a/beetsplug/acousticbrainz.py +++ b/beetsplug/acousticbrainz.py @@ -153,7 +153,7 @@ class AcousticPlugin(plugins.BeetsPlugin): try: data.update(res.json()) except ValueError: - self._log.debug("Invalid Response: {}", res.text) + self._log.debug("Invalid Response: {.text}", res) return {} return data diff --git a/beetsplug/badfiles.py b/beetsplug/badfiles.py index 7b63a7496..070008be8 100644 --- a/beetsplug/badfiles.py +++ b/beetsplug/badfiles.py @@ -127,12 +127,11 @@ class BadFiles(BeetsPlugin): except CheckerCommandError as e: if e.errno == errno.ENOENT: self._log.error( - "command not found: {} when validating file: {}", - e.checker, - e.path, + "command not found: {0.checker} when validating file: {0.path}", + e, ) else: - self._log.error("error invoking {}: {}", e.checker, e.msg) + self._log.error("error invoking {0.checker}: {0.msg}", e) return [] error_lines = [] diff --git a/beetsplug/bpd/__init__.py b/beetsplug/bpd/__init__.py index bd90f4d6b..aa7013150 100644 --- a/beetsplug/bpd/__init__.py +++ b/beetsplug/bpd/__init__.py @@ -763,7 +763,7 @@ class Connection: def debug(self, message, kind=" "): """Log a debug message about this connection.""" - self.server._log.debug("{}[{}]: {}", kind, self.address, message) + self.server._log.debug("{}[{.address}]: {}", kind, self, message) def run(self): pass @@ -911,7 +911,7 @@ class ControlConnection(Connection): super().__init__(server, sock) def debug(self, message, kind=" "): - self.server._log.debug("CTRL {}[{}]: {}", kind, self.address, message) + self.server._log.debug("CTRL {}[{.address}]: {}", kind, self, message) def run(self): """Listen for control commands and delegate to `ctrl_*` methods.""" diff --git a/beetsplug/bpsync.py b/beetsplug/bpsync.py index ccd781b28..9ae6d47d5 100644 --- a/beetsplug/bpsync.py +++ b/beetsplug/bpsync.py @@ -82,8 +82,8 @@ class BPSyncPlugin(BeetsPlugin): if not self.is_beatport_track(item): self._log.info( - "Skipping non-{} singleton: {}", - self.beatport_plugin.data_source, + "Skipping non-{.beatport_plugin.data_source} singleton: {}", + self, item, ) continue @@ -107,8 +107,8 @@ class BPSyncPlugin(BeetsPlugin): return False if not album.mb_albumid.isnumeric(): self._log.info( - "Skipping album with invalid {} ID: {}", - self.beatport_plugin.data_source, + "Skipping album with invalid {.beatport_plugin.data_source} ID: {}", + self, album, ) return False @@ -117,8 +117,8 @@ class BPSyncPlugin(BeetsPlugin): return items if not all(self.is_beatport_track(item) for item in items): self._log.info( - "Skipping non-{} release: {}", - self.beatport_plugin.data_source, + "Skipping non-{.beatport_plugin.data_source} release: {}", + self, album, ) return False @@ -139,9 +139,7 @@ class BPSyncPlugin(BeetsPlugin): albuminfo = self.beatport_plugin.album_for_id(album.mb_albumid) if not albuminfo: self._log.info( - "Release ID {} not found for album {}", - album.mb_albumid, - album, + "Release ID {0.mb_albumid} not found for album {0}", album ) continue diff --git a/beetsplug/chroma.py b/beetsplug/chroma.py index 43f556f05..192310fb8 100644 --- a/beetsplug/chroma.py +++ b/beetsplug/chroma.py @@ -343,20 +343,20 @@ def fingerprint_item(log, item, write=False): """ # Get a fingerprint and length for this track. if not item.length: - log.info("{}: no duration available", item.filepath) + log.info("{.filepath}: no duration available", item) elif item.acoustid_fingerprint: if write: - log.info("{}: fingerprint exists, skipping", item.filepath) + log.info("{.filepath}: fingerprint exists, skipping", item) else: - log.info("{}: using existing fingerprint", item.filepath) + log.info("{.filepath}: using existing fingerprint", item) return item.acoustid_fingerprint else: - log.info("{}: fingerprinting", item.filepath) + log.info("{.filepath}: fingerprinting", item) try: _, fp = acoustid.fingerprint_file(util.syspath(item.path)) item.acoustid_fingerprint = fp.decode() if write: - log.info("{}: writing fingerprint", item.filepath) + log.info("{.filepath}: writing fingerprint", item) item.try_write() if item._db: item.store() diff --git a/beetsplug/convert.py b/beetsplug/convert.py index 25c3a6f0c..e9db3592e 100644 --- a/beetsplug/convert.py +++ b/beetsplug/convert.py @@ -319,10 +319,9 @@ class ConvertPlugin(BeetsPlugin): util.displayable_path(source), ) self._log.debug( - "Command {} exited with status {}: {}", + "Command {0} exited with status {1.returncode}: {1.output}", args, - exc.returncode, - exc.output, + exc, ) util.remove(dest) util.prune_dirs(os.path.dirname(dest)) @@ -388,15 +387,15 @@ class ConvertPlugin(BeetsPlugin): if os.path.exists(util.syspath(dest)): self._log.info( - "Skipping {} (target file exists)", item.filepath + "Skipping {.filepath} (target file exists)", item ) continue if keep_new: if pretend: self._log.info( - "mv {} {}", - item.filepath, + "mv {.filepath} {}", + item, util.displayable_path(original), ) else: @@ -430,7 +429,7 @@ class ConvertPlugin(BeetsPlugin): else ("Linking" if link else "Copying") ) - self._log.info("{} {}", msg, item.filepath) + self._log.info("{} {.filepath}", msg, item) if hardlink: util.hardlink(original, converted) @@ -461,7 +460,7 @@ class ConvertPlugin(BeetsPlugin): if album and album.artpath: maxwidth = self._get_art_resize(album.artpath) self._log.debug( - "embedding album art from {}", album.art_filepath + "embedding album art from {.art_filepath}", album ) art.embed_item( self._log, @@ -519,7 +518,7 @@ class ConvertPlugin(BeetsPlugin): if os.path.exists(util.syspath(dest)): self._log.info( - "Skipping {} (target file exists)", album.art_filepath + "Skipping {.art_filepath} (target file exists)", album ) return @@ -529,8 +528,8 @@ class ConvertPlugin(BeetsPlugin): # Either copy or resize (while copying) the image. if maxwidth is not None: self._log.info( - "Resizing cover art from {} to {}", - album.art_filepath, + "Resizing cover art from {.art_filepath} to {}", + album, util.displayable_path(dest), ) if not pretend: @@ -540,9 +539,9 @@ class ConvertPlugin(BeetsPlugin): msg = "ln" if hardlink else ("ln -s" if link else "cp") self._log.info( - "{} {} {}", + "{} {.art_filepath} {}", msg, - album.art_filepath, + album, util.displayable_path(dest), ) else: @@ -553,9 +552,9 @@ class ConvertPlugin(BeetsPlugin): ) self._log.info( - "{} cover art from {} to {}", + "{} cover art from {.art_filepath} to {}", msg, - album.art_filepath, + album, util.displayable_path(dest), ) if hardlink: diff --git a/beetsplug/deezer.py b/beetsplug/deezer.py index def2b37a2..e427b08b1 100644 --- a/beetsplug/deezer.py +++ b/beetsplug/deezer.py @@ -251,16 +251,16 @@ class DeezerPlugin(SearchApiMetadataSourcePlugin[IDResponse]): response.raise_for_status() except requests.exceptions.RequestException as e: self._log.error( - "Error fetching data from {} API\n Error: {}", - self.data_source, + "Error fetching data from {.data_source} API\n Error: {}", + self, e, ) return () response_data: Sequence[IDResponse] = response.json().get("data", []) self._log.debug( - "Found {} result(s) from {} for '{}'", + "Found {} result(s) from {.data_source} for '{}'", len(response_data), - self.data_source, + self, query, ) return response_data diff --git a/beetsplug/duplicates.py b/beetsplug/duplicates.py index 03714ab81..904e19262 100644 --- a/beetsplug/duplicates.py +++ b/beetsplug/duplicates.py @@ -254,24 +254,24 @@ class DuplicatesPlugin(BeetsPlugin): checksum = getattr(item, key, False) if not checksum: self._log.debug( - "key {} on item {} not cached:computing checksum", + "key {} on item {.filepath} not cached:computing checksum", key, - item.filepath, + item, ) try: checksum = command_output(args).stdout setattr(item, key, checksum) item.store() self._log.debug( - "computed checksum for {} using {}", item.title, key + "computed checksum for {.title} using {}", item, key ) except subprocess.CalledProcessError as e: - self._log.debug("failed to checksum {}: {}", item.filepath, e) + self._log.debug("failed to checksum {.filepath}: {}", item, e) else: self._log.debug( - "key {} on item {} cached:not computing checksum", + "key {} on item {.filepath} cached:not computing checksum", key, - item.filepath, + item, ) return key, checksum @@ -289,15 +289,15 @@ class DuplicatesPlugin(BeetsPlugin): values = [v for v in values if v not in (None, "")] if strict and len(values) < len(keys): self._log.debug( - "some keys {} on item {} are null or empty: skipping", + "some keys {} on item {.filepath} are null or empty: skipping", keys, - obj.filepath, + obj, ) elif not strict and not len(values): self._log.debug( - "all keys {} on item {} are null or empty: skipping", + "all keys {} on item {.filepath} are null or empty: skipping", keys, - obj.filepath, + obj, ) else: key = tuple(values) @@ -356,10 +356,10 @@ class DuplicatesPlugin(BeetsPlugin): if value: self._log.debug( "key {} on item {} is null " - "or empty: setting from item {}", + "or empty: setting from item {.filepath}", f, displayable_path(objs[0].path), - o.filepath, + o, ) setattr(objs[0], f, value) objs[0].store() @@ -380,10 +380,10 @@ class DuplicatesPlugin(BeetsPlugin): missing.add(i._db) self._log.debug( "item {} missing from album {}:" - " merging from {} into {}", + " merging from {.filepath} into {}", missing, objs[0], - o.filepath, + o, displayable_path(missing.destination()), ) missing.move(operation=MoveOperation.COPY) diff --git a/beetsplug/fetchart.py b/beetsplug/fetchart.py index 156dd6132..54c065da4 100644 --- a/beetsplug/fetchart.py +++ b/beetsplug/fetchart.py @@ -133,7 +133,7 @@ class Candidate: # get_size returns None if no local imaging backend is available if not self.size: self.size = ArtResizer.shared.get_size(self.path) - self._log.debug("image size: {}", self.size) + self._log.debug("image size: {.size}", self) if not self.size: self._log.warning( @@ -151,7 +151,7 @@ class Candidate: # Check minimum dimension. if plugin.minwidth and self.size[0] < plugin.minwidth: self._log.debug( - "image too small ({} < {})", self.size[0], plugin.minwidth + "image too small ({} < {.minwidth})", self.size[0], plugin ) return ImageAction.BAD @@ -162,10 +162,10 @@ class Candidate: if edge_diff > plugin.margin_px: self._log.debug( "image is not close enough to being " - "square, ({} - {} > {})", + "square, ({} - {} > {.margin_px})", long_edge, short_edge, - plugin.margin_px, + plugin, ) return ImageAction.BAD elif plugin.margin_percent: @@ -190,7 +190,7 @@ class Candidate: downscale = False if plugin.maxwidth and self.size[0] > plugin.maxwidth: self._log.debug( - "image needs rescaling ({} > {})", self.size[0], plugin.maxwidth + "image needs rescaling ({} > {.maxwidth})", self.size[0], plugin ) downscale = True @@ -200,9 +200,9 @@ class Candidate: filesize = os.stat(syspath(self.path)).st_size if filesize > plugin.max_filesize: self._log.debug( - "image needs resizing ({}B > {}B)", + "image needs resizing ({}B > {.max_filesize}B)", filesize, - plugin.max_filesize, + plugin, ) downsize = True @@ -213,9 +213,9 @@ class Candidate: reformat = fmt != plugin.cover_format if reformat: self._log.debug( - "image needs reformatting: {} -> {}", + "image needs reformatting: {} -> {.cover_format}", fmt, - plugin.cover_format, + plugin, ) skip_check_for = skip_check_for or [] @@ -329,7 +329,7 @@ def _logged_get(log: Logger, *args, **kwargs) -> requests.Response: prepped.url, {}, None, None, None ) send_kwargs.update(settings) - log.debug("{}: {}", message, prepped.url) + log.debug("{}: {.url}", message, prepped) return s.send(prepped, **send_kwargs) @@ -542,14 +542,14 @@ class CoverArtArchive(RemoteArtSource): try: response = self.request(url) except requests.RequestException: - self._log.debug("{}: error receiving response", self.NAME) + self._log.debug("{.NAME}: error receiving response", self) return try: data = response.json() except ValueError: self._log.debug( - "{}: error loading response: {}", self.NAME, response.text + "{.NAME}: error loading response: {.text}", self, response ) return @@ -629,7 +629,7 @@ class AlbumArtOrg(RemoteArtSource): # Get the page from albumart.org. try: resp = self.request(self.URL, params={"asin": album.asin}) - self._log.debug("scraped art URL: {}", resp.url) + self._log.debug("scraped art URL: {.url}", resp) except requests.RequestException: self._log.debug("error scraping art page") return @@ -702,7 +702,7 @@ class GoogleImages(RemoteArtSource): try: data = response.json() except ValueError: - self._log.debug("google: error loading response: {}", response.text) + self._log.debug("google: error loading response: {.text}", response) return if "error" in data: @@ -764,7 +764,7 @@ class FanartTV(RemoteArtSource): data = response.json() except ValueError: self._log.debug( - "fanart.tv: error loading response: {}", response.text + "fanart.tv: error loading response: {.text}", response ) return @@ -953,8 +953,8 @@ class Wikipedia(RemoteArtSource): self._log.debug("wikipedia: album not found on dbpedia") except (ValueError, KeyError, IndexError): self._log.debug( - "wikipedia: error scraping dbpedia response: {}", - dbpedia_response.text, + "wikipedia: error scraping dbpedia response: {.text}", + dbpedia_response, ) # Ensure we have a filename before attempting to query wikipedia @@ -1179,7 +1179,7 @@ class LastFM(RemoteArtSource): if "error" in data: if data["error"] == 6: self._log.debug( - "lastfm: no results for {}", album.mb_albumid + "lastfm: no results for {.mb_albumid}", album ) else: self._log.error( @@ -1200,7 +1200,7 @@ class LastFM(RemoteArtSource): url=images[size], size=self.SIZES[size] ) except ValueError: - self._log.debug("lastfm: error loading response: {}", response.text) + self._log.debug("lastfm: error loading response: {.text}", response) return @@ -1244,7 +1244,7 @@ class Spotify(RemoteArtSource): soup = BeautifulSoup(html, "html.parser") except ValueError: self._log.debug( - "Spotify: error loading response: {}", response.text + "Spotify: error loading response: {.text}", response ) return @@ -1541,7 +1541,7 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin): out = candidate assert out.path is not None # help mypy self._log.debug( - "using {.LOC} image {}", source, out.path + "using {.LOC} image {.path}", source, out ) break # Remove temporary files for invalid candidates. diff --git a/beetsplug/ftintitle.py b/beetsplug/ftintitle.py index 6571c9c90..f0f088099 100644 --- a/beetsplug/ftintitle.py +++ b/beetsplug/ftintitle.py @@ -150,10 +150,10 @@ class FtInTitlePlugin(plugins.BeetsPlugin): # In case the artist is kept, do not update the artist fields. if keep_in_artist_field: self._log.info( - "artist: {} (Not changing due to keep_in_artist)", item.artist + "artist: {.artist} (Not changing due to keep_in_artist)", item ) else: - self._log.info("artist: {} -> {}", item.artist, item.albumartist) + self._log.info("artist: {0.artist} -> {0.albumartist}", item) item.artist = item.albumartist if item.artist_sort: @@ -166,7 +166,7 @@ class FtInTitlePlugin(plugins.BeetsPlugin): feat_format = self.config["format"].as_str() new_format = feat_format.format(feat_part) new_title = f"{item.title} {new_format}" - self._log.info("title: {} -> {}", item.title, new_title) + self._log.info("title: {.title} -> {}", item, new_title) item.title = new_title def ft_in_title( @@ -194,7 +194,7 @@ class FtInTitlePlugin(plugins.BeetsPlugin): if not featured: return False - self._log.info("{}", item.filepath) + self._log.info("{.filepath}", item) # Attempt to find the featured artist. feat_part = find_feat_part(artist, albumartist) diff --git a/beetsplug/hook.py b/beetsplug/hook.py index 62cb86567..b8869eca4 100644 --- a/beetsplug/hook.py +++ b/beetsplug/hook.py @@ -83,7 +83,7 @@ class HookPlugin(BeetsPlugin): subprocess.check_call(command_pieces) except subprocess.CalledProcessError as exc: self._log.error( - "hook for {} exited with status {}", event, exc.returncode + "hook for {} exited with status {.returncode}", event, exc ) except OSError as exc: self._log.error("hook for {} failed: {}", event, exc) diff --git a/beetsplug/importadded.py b/beetsplug/importadded.py index b6244c518..f728a104f 100644 --- a/beetsplug/importadded.py +++ b/beetsplug/importadded.py @@ -103,9 +103,9 @@ class ImportAddedPlugin(BeetsPlugin): def update_album_times(self, lib, album): if self.reimported_album(album): self._log.debug( - "Album '{}' is reimported, skipping import of " + "Album '{.filepath}' is reimported, skipping import of " "added dates for the album and its items.", - album.filepath, + album, ) return @@ -119,18 +119,17 @@ class ImportAddedPlugin(BeetsPlugin): item.store() album.added = min(album_mtimes) self._log.debug( - "Import of album '{}', selected album.added={} " + "Import of album '{0.album}', selected album.added={0.added} " "from item file mtimes.", - album.album, - album.added, + album, ) album.store() def update_item_times(self, lib, item): if self.reimported_item(item): self._log.debug( - "Item '{}' is reimported, skipping import of added date.", - item.filepath, + "Item '{.filepath}' is reimported, skipping import of added date.", + item, ) return mtime = self.item_mtime.pop(item.path, None) @@ -139,9 +138,8 @@ class ImportAddedPlugin(BeetsPlugin): if self.config["preserve_mtimes"].get(bool): self.write_item_mtime(item, mtime) self._log.debug( - "Import of item '{}', selected item.added={}", - item.filepath, - item.added, + "Import of item '{0.filepath}', selected item.added={0.added}", + item, ) item.store() @@ -153,7 +151,6 @@ class ImportAddedPlugin(BeetsPlugin): if self.config["preserve_write_mtimes"].get(bool): self.write_item_mtime(item, item.added) self._log.debug( - "Write of item '{}', selected item.added={}", - item.filepath, - item.added, + "Write of item '{0.filepath}', selected item.added={0.added}", + item, ) diff --git a/beetsplug/ipfs.py b/beetsplug/ipfs.py index 8b1b77a3e..8b6d57fd3 100644 --- a/beetsplug/ipfs.py +++ b/beetsplug/ipfs.py @@ -180,7 +180,7 @@ class IPFSPlugin(BeetsPlugin): util.command_output(cmd) except (OSError, subprocess.CalledProcessError) as err: self._log.error( - "Failed to get {} from ipfs.\n{}", _hash, err.output + "Failed to get {} from ipfs.\n{.output}", _hash, err ) return False diff --git a/beetsplug/keyfinder.py b/beetsplug/keyfinder.py index 43f04e263..e2aff24e5 100644 --- a/beetsplug/keyfinder.py +++ b/beetsplug/keyfinder.py @@ -73,7 +73,7 @@ class KeyFinderPlugin(BeetsPlugin): except IndexError: # Sometimes keyfinder-cli returns 0 but with no key, usually # when the file is silent or corrupt, so we log and skip. - self._log.error("no key returned for path: {}", item.path) + self._log.error("no key returned for path: {.path}", item) continue try: @@ -84,7 +84,7 @@ class KeyFinderPlugin(BeetsPlugin): item["initial_key"] = key self._log.info( - "added computed initial key {} for {}", key, item.filepath + "added computed initial key {} for {.filepath}", key, item ) if write: diff --git a/beetsplug/lastimport.py b/beetsplug/lastimport.py index 039b5dfc4..baa522d14 100644 --- a/beetsplug/lastimport.py +++ b/beetsplug/lastimport.py @@ -268,10 +268,9 @@ def process_tracks(lib, tracks, log): count = int(song.get("play_count", 0)) new_count = int(tracks[num].get("playcount", 1)) log.debug( - "match: {} - {} ({}) updating: play_count {} => {}", - song.artist, - song.title, - song.album, + "match: {0.artist} - {0.title} ({0.album}) updating:" + " play_count {1} => {2}", + song, count, new_count, ) diff --git a/beetsplug/lyrics.py b/beetsplug/lyrics.py index 7b5ee55c7..bd451db59 100644 --- a/beetsplug/lyrics.py +++ b/beetsplug/lyrics.py @@ -508,9 +508,9 @@ class SearchBackend(SoupMixin, Backend): # log out the candidate that did not make it but was close. # This may show a matching candidate with some noise in the name self.debug( - "({}, {}) does not match ({}, {}) but dist was close: {:.2f}", - result.artist, - result.title, + "({0.artist}, {0.title}) does not match ({1}, {2}) but dist" + " was close: {3:.2f}", + result, target_artist, target_title, max_dist, @@ -838,15 +838,16 @@ class Translator(RequestHandler): lyrics_language = langdetect.detect(new_lyrics).upper() if lyrics_language == self.to_language: self.info( - "🔵 Lyrics are already in the target language {}", - self.to_language, + "🔵 Lyrics are already in the target language {.to_language}", + self, ) return new_lyrics if self.from_languages and lyrics_language not in self.from_languages: self.info( - "🔵 Configuration {} does not permit translating from {}", - self.from_languages, + "🔵 Configuration {.from_languages} does not permit translating" + " from {}", + self, lyrics_language, ) return new_lyrics @@ -854,7 +855,7 @@ class Translator(RequestHandler): lyrics, *url = new_lyrics.split("\n\nSource: ") with self.handle_request(): translated_lines = self.append_translations(lyrics.splitlines()) - self.info("🟢 Translated lyrics to {}", self.to_language) + self.info("🟢 Translated lyrics to {.to_language}", self) return "\n\nSource: ".join(["\n".join(translated_lines), *url]) diff --git a/beetsplug/missing.py b/beetsplug/missing.py index c440f6c69..cbdda4599 100644 --- a/beetsplug/missing.py +++ b/beetsplug/missing.py @@ -226,8 +226,8 @@ class MissingPlugin(BeetsPlugin): for track_info in album_info.tracks: if track_info.track_id not in item_mbids: self._log.debug( - "track {} in album {}", - track_info.track_id, - album_info.album_id, + "track {.track_id} in album {.album_id}", + track_info, + album_info, ) yield _item(track_info, album_info, album.id) diff --git a/beetsplug/mpdstats.py b/beetsplug/mpdstats.py index f4fe21a13..0a3e1de02 100644 --- a/beetsplug/mpdstats.py +++ b/beetsplug/mpdstats.py @@ -51,8 +51,8 @@ class MPDClientWrapper: if not self.strip_path.endswith("/"): self.strip_path += "/" - self._log.debug("music_directory: {}", self.music_directory) - self._log.debug("strip_path: {}", self.strip_path) + self._log.debug("music_directory: {.music_directory}", self) + self._log.debug("strip_path: {.strip_path}", self) self.client = mpd.MPDClient() @@ -188,10 +188,10 @@ class MPDStats: item.store() self._log.debug( - "updated: {} = {} [{}]", + "updated: {} = {} [{.filepath}]", attribute, item[attribute], - item.filepath, + item, ) def update_rating(self, item, skipped): diff --git a/beetsplug/mpdupdate.py b/beetsplug/mpdupdate.py index e92d5c63f..5d8fc598b 100644 --- a/beetsplug/mpdupdate.py +++ b/beetsplug/mpdupdate.py @@ -101,8 +101,8 @@ class MPDUpdatePlugin(BeetsPlugin): try: s = BufferedSocket(host, port) - except OSError as e: - self._log.warning("MPD connection failed: {}", str(e.strerror)) + except OSError: + self._log.warning("MPD connection failed", exc_info=True) return resp = s.readline() diff --git a/beetsplug/parentwork.py b/beetsplug/parentwork.py index 5b5f215f3..eb2fd8f11 100644 --- a/beetsplug/parentwork.py +++ b/beetsplug/parentwork.py @@ -179,9 +179,8 @@ class ParentWorkPlugin(BeetsPlugin): if not item.mb_workid: self._log.info( - "No work for {}, add one at https://musicbrainz.org/recording/{}", + "No work for {0}, add one at https://musicbrainz.org/recording/{0.mb_trackid}", item, - item.mb_trackid, ) return diff --git a/beetsplug/playlist.py b/beetsplug/playlist.py index 01625c281..07c12e0e0 100644 --- a/beetsplug/playlist.py +++ b/beetsplug/playlist.py @@ -142,7 +142,7 @@ class PlaylistPlugin(beets.plugins.BeetsPlugin): dir_contents = os.listdir(playlist_dir) except OSError: self._log.warning( - "Unable to open playlist directory {}", self.playlist_dir + "Unable to open playlist directory {.playlist_dir}", self ) return diff --git a/beetsplug/replaygain.py b/beetsplug/replaygain.py index 39bbf2e17..3e777d977 100644 --- a/beetsplug/replaygain.py +++ b/beetsplug/replaygain.py @@ -141,9 +141,8 @@ class RgTask: item.rg_track_peak = track_gain.peak item.store() self._log.debug( - "applied track gain {} LU, peak {} of FS", - item.rg_track_gain, - item.rg_track_peak, + "applied track gain {0.rg_track_gain} LU, peak {0.rg_track_peak} of FS", + item, ) def _store_album_gain(self, item: Item, album_gain: Gain): @@ -155,9 +154,8 @@ class RgTask: item.rg_album_peak = album_gain.peak item.store() self._log.debug( - "applied album gain {} LU, peak {} of FS", - item.rg_album_gain, - item.rg_album_peak, + "applied album gain {0.rg_album_gain} LU, peak {0.rg_album_peak} of FS", + item, ) def _store_track(self, write: bool): @@ -230,7 +228,7 @@ class R128Task(RgTask): def _store_track_gain(self, item: Item, track_gain: Gain): item.r128_track_gain = track_gain.gain item.store() - self._log.debug("applied r128 track gain {} LU", item.r128_track_gain) + self._log.debug("applied r128 track gain {.r128_track_gain} LU", item) def _store_album_gain(self, item: Item, album_gain: Gain): """ @@ -239,7 +237,7 @@ class R128Task(RgTask): """ item.r128_album_gain = album_gain.gain item.store() - self._log.debug("applied r128 album gain {} LU", item.r128_album_gain) + self._log.debug("applied r128 album gain {.r128_album_gain} LU", item) AnyRgTask = TypeVar("AnyRgTask", bound=RgTask) @@ -380,7 +378,7 @@ class FfmpegBackend(Backend): album_gain = target_level_lufs - album_gain self._log.debug( - "{}: gain {} LU, peak {}", task.album, album_gain, album_peak + "{.album}: gain {} LU, peak {}", task, album_gain, album_peak ) task.album_gain = Gain(album_gain, album_peak) @@ -1093,9 +1091,8 @@ class AudioToolsBackend(Backend): ) self._log.debug( - "ReplayGain for track {} - {}: {2:.2f}, {3:.2f}", - item.artist, - item.title, + "ReplayGain for track {0.artist} - {0.title}: {1:.2f}, {2:.2f}", + item, rg_track_gain, rg_track_peak, ) @@ -1133,8 +1130,8 @@ class AudioToolsBackend(Backend): rg_album_gain, task.target_level ) self._log.debug( - "ReplayGain for album {}: {.2f}, {.2f}", - task.items[0].album, + "ReplayGain for album {.items[0].album}: {.2f}, {.2f}", + task, rg_album_gain, rg_album_peak, ) diff --git a/beetsplug/scrub.py b/beetsplug/scrub.py index 66d5aeea3..c39894137 100644 --- a/beetsplug/scrub.py +++ b/beetsplug/scrub.py @@ -59,7 +59,7 @@ class ScrubPlugin(BeetsPlugin): def scrub_func(lib, opts, args): # Walk through matching files and remove tags. for item in lib.items(args): - self._log.info("scrubbing: {}", item.filepath) + self._log.info("scrubbing: {.filepath}", item) self._scrub_item(item, opts.write) scrub_cmd = ui.Subcommand("scrub", help="clean audio tags") @@ -147,5 +147,5 @@ class ScrubPlugin(BeetsPlugin): def import_task_files(self, session, task): """Automatically scrub imported files.""" for item in task.imported_items(): - self._log.debug("auto-scrubbing {}", item.filepath) + self._log.debug("auto-scrubbing {.filepath}", item) self._scrub_item(item, ui.should_write()) diff --git a/beetsplug/spotify.py b/beetsplug/spotify.py index 897899301..d83927328 100644 --- a/beetsplug/spotify.py +++ b/beetsplug/spotify.py @@ -188,9 +188,7 @@ class SpotifyPlugin( self.access_token = response.json()["access_token"] # Save the token for later use. - self._log.debug( - "{} access token: {}", self.data_source, self.access_token - ) + self._log.debug("{0.data_source} access token: {0.access_token}", self) with open(self._tokenfile(), "w") as f: json.dump({"access_token": self.access_token}, f) @@ -451,9 +449,9 @@ class SpotifyPlugin( return () response_data = response.get(f"{query_type}s", {}).get("items", []) self._log.debug( - "Found {} result(s) from {} for '{}'", + "Found {} result(s) from {.data_source} for '{}'", len(response_data), - self.data_source, + self, query, ) return response_data @@ -539,8 +537,8 @@ class SpotifyPlugin( if not items: self._log.debug( - "Your beets query returned no items, skipping {}.", - self.data_source, + "Your beets query returned no items, skipping {.data_source}.", + self, ) return @@ -595,8 +593,8 @@ class SpotifyPlugin( or self.config["tiebreak"].get() == "first" ): self._log.debug( - "{} track(s) found, count: {}", - self.data_source, + "{.data_source} track(s) found, count: {}", + self, len(response_data_tracks), ) chosen_result = response_data_tracks[0] @@ -619,19 +617,19 @@ class SpotifyPlugin( if failure_count > 0: if self.config["show_failures"].get(): self._log.info( - "{} track(s) did not match a {} ID:", + "{} track(s) did not match a {.data_source} ID:", failure_count, - self.data_source, + self, ) for track in failures: self._log.info("track: {}", track) self._log.info("") else: self._log.warning( - "{} track(s) did not match a {} ID:\n" + "{} track(s) did not match a {.data_source} ID:\n" "use --show-failures to display", failure_count, - self.data_source, + self, ) return results diff --git a/beetsplug/subsonicupdate.py b/beetsplug/subsonicupdate.py index 683574aaa..673cc94a8 100644 --- a/beetsplug/subsonicupdate.py +++ b/beetsplug/subsonicupdate.py @@ -108,7 +108,7 @@ class SubsonicUpdate(BeetsPlugin): auth = self.config["auth"].as_str() url = self.__format_url("startScan") self._log.debug("URL is {}", url) - self._log.debug("auth type is {}", self.config["auth"]) + self._log.debug("auth type is {.config[auth]}", self) if auth == "token": salt, token = self.__create_token() diff --git a/beetsplug/thumbnails.py b/beetsplug/thumbnails.py index e5094a87d..651eaf3ac 100644 --- a/beetsplug/thumbnails.py +++ b/beetsplug/thumbnails.py @@ -127,7 +127,7 @@ class ThumbnailsPlugin(BeetsPlugin): size = ArtResizer.shared.get_size(album.artpath) if not size: self._log.warning( - "problem getting the picture size for {}", album.artpath + "problem getting the picture size for {.artpath}", album ) return diff --git a/pyproject.toml b/pyproject.toml index 6691221b5..3ba7b8b6a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -275,6 +275,7 @@ select = [ "E", # pycodestyle "F", # pyflakes # "B", # flake8-bugbear + "G", # flake8-logging-format "I", # isort "ISC", # flake8-implicit-str-concat "N", # pep8-naming