From aced802c5644722c8ca87f3419f463541d22a0a8 Mon Sep 17 00:00:00 2001 From: Gabriel Push Date: Thu, 20 Nov 2025 15:57:22 -0500 Subject: [PATCH] Fix recursion in inline plugin when item_fields shadow DB fields (#6115) --- beetsplug/inline.py | 13 +++++++++---- test/plugins/test_plugins.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 test/plugins/test_plugins.py diff --git a/beetsplug/inline.py b/beetsplug/inline.py index e9a94ac38..860a205ee 100644 --- a/beetsplug/inline.py +++ b/beetsplug/inline.py @@ -61,18 +61,18 @@ class InlinePlugin(BeetsPlugin): config["item_fields"].items(), config["pathfields"].items() ): self._log.debug("adding item field {}", key) - func = self.compile_inline(view.as_str(), False) + func = self.compile_inline(view.as_str(), False, key) if func is not None: self.template_fields[key] = func # Album fields. for key, view in config["album_fields"].items(): self._log.debug("adding album field {}", key) - func = self.compile_inline(view.as_str(), True) + func = self.compile_inline(view.as_str(), True, key) if func is not None: self.album_template_fields[key] = func - def compile_inline(self, python_code, album): + def compile_inline(self, python_code, album, field_name): """Given a Python expression or function body, compile it as a path field function. The returned function takes a single argument, an Item, and returns a Unicode string. If the expression cannot be @@ -97,7 +97,12 @@ class InlinePlugin(BeetsPlugin): is_expr = True def _dict_for(obj): - out = dict(obj) + out = {} + for key in obj.keys(computed=False): + if key == field_name: + continue + out[key] = obj._get(key) + if album: out["items"] = list(obj.items()) return out diff --git a/test/plugins/test_plugins.py b/test/plugins/test_plugins.py new file mode 100644 index 000000000..a606f16ca --- /dev/null +++ b/test/plugins/test_plugins.py @@ -0,0 +1,31 @@ +# test/plugins/test_plugins.py + +from beets import config, plugins +from beets.test.helper import PluginTestCase + +class TestInlineRecursion(PluginTestCase): + def test_no_recursion_when_inline_shadows_fixed_field(self): + config['plugins'] = ['inline'] + + config['item_fields'] = { + 'track_no': ( + "f'{disc:02d}-{track:02d}' if disctotal > 1 " + "else f'{track:02d}'" + ) + } + + plugins._instances.clear() + plugins.load_plugins() + + item = self.add_item_fixture( + artist='Artist', + album='Album', + title='Title', + track=1, + disc=1, + disctotal=1, + ) + + out = item.evaluate_template('$track_no') + + assert out == '01' \ No newline at end of file