Skip to content

Commit cbf389c

Browse files
committed
Added test for the error handling.
1 parent 677b552 commit cbf389c

File tree

2 files changed

+89
-2
lines changed

2 files changed

+89
-2
lines changed

beets/metadata_plugins.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,11 @@ def _safe_call(
154154
try:
155155
return func(*arg, **kwargs)
156156
except Exception as e:
157-
log.error(f"Error in metadata source plugin {func.__name__}: {e}")
157+
log.error(
158+
"Error in '{}': {}",
159+
__class_name_from_method(func),
160+
e,
161+
)
158162
log.debug("Exception details:", exc_info=True)
159163

160164
return None
@@ -167,10 +171,23 @@ def _safe_yield_from(
167171
try:
168172
yield from func(*arg, **kwargs)
169173
except Exception as e:
170-
log.error(f"Error in metadata source plugin {func.__name__}: {e}")
174+
log.error(
175+
"Error in '{}': {}",
176+
__class_name_from_method(func),
177+
e,
178+
)
171179
log.debug("Exception details:", exc_info=True)
172180

173181

182+
def __class_name_from_method(func: Callable) -> str:
183+
"""Helper function to get the class name from a method."""
184+
return (
185+
func.__qualname__.split(".")[0]
186+
if "." in func.__qualname__
187+
else "Unknown"
188+
)
189+
190+
174191
def _get_distance(
175192
config: ConfigView, data_source: str, info: AlbumInfo | TrackInfo
176193
) -> Distance:

test/test_metadata_plugins.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
from typing import Iterable
2+
3+
import pytest
4+
from confuse import AttrDict
5+
6+
from beets import metadata_plugins
7+
from beets.test.helper import PluginMixin
8+
9+
10+
class ErrorMetadataMockPlugin(metadata_plugins.MetadataSourcePlugin):
11+
"""A metadata source plugin that raises errors in all its methods."""
12+
13+
def candidates(self, *args, **kwargs):
14+
raise ValueError("Mocked error")
15+
16+
def item_candidates(self, *args, **kwargs):
17+
raise ValueError("Mocked error")
18+
19+
def album_for_id(self, *args, **kwargs):
20+
raise ValueError("Mocked error")
21+
22+
def track_for_id(self, *args, **kwargs):
23+
raise ValueError("Mocked error")
24+
25+
def track_distance(self, *args, **kwargs):
26+
raise ValueError("Mocked error")
27+
28+
def album_distance(self, *args, **kwargs):
29+
raise ValueError("Mocked error")
30+
31+
32+
class TestMetadataPluginsException(PluginMixin):
33+
"""Check that errors during the metadata plugins do not crash beets.
34+
They should be logged as errors instead.
35+
"""
36+
37+
@pytest.fixture(autouse=True)
38+
def setup(self):
39+
self.register_plugin(ErrorMetadataMockPlugin)
40+
yield
41+
self.unload_plugins()
42+
43+
@pytest.mark.parametrize(
44+
"method_name,args",
45+
[
46+
("candidates", ()),
47+
("item_candidates", ()),
48+
("album_for_id", ("some_id",)),
49+
("track_for_id", ("some_id",)),
50+
("track_distance", (None, AttrDict({"data_source": "mock"}))),
51+
("album_distance", (None, AttrDict({"data_source": "mock"}), None)),
52+
],
53+
)
54+
def test_error_handling_candidates(
55+
self,
56+
caplog,
57+
method_name,
58+
args,
59+
):
60+
with caplog.at_level("ERROR"):
61+
# Call the method to trigger the error
62+
ret = getattr(metadata_plugins, method_name)(*args)
63+
if isinstance(ret, Iterable):
64+
list(ret)
65+
66+
# Check that an error was logged
67+
assert len(caplog.records) == 1
68+
logs = [record.getMessage() for record in caplog.records]
69+
assert logs == ["Error in 'ErrorMetadataMockPlugin': Mocked error"]
70+
caplog.clear()

0 commit comments

Comments
 (0)