From 9840964f51fed3b3d0dcc1db9c76f90be4c05e73 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Tue, 16 May 2017 14:00:10 -0400 Subject: [PATCH] Fix #2562: avoid crash with newlines in templates Turns out! The $ character in Python regexes also matches before the last newline at the end of a string, not just at the end of a string. The \Z entity does what we really want: the *real* end of the string. --- beets/util/functemplate.py | 9 ++++++--- docs/changelog.rst | 1 + test/test_template.py | 5 +++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/beets/util/functemplate.py b/beets/util/functemplate.py index 58b0416a1..0e13db4a0 100644 --- a/beets/util/functemplate.py +++ b/beets/util/functemplate.py @@ -325,7 +325,7 @@ class Parser(object): # Common parsing resources. special_chars = (SYMBOL_DELIM, FUNC_DELIM, GROUP_OPEN, GROUP_CLOSE, ESCAPE_CHAR) - special_char_re = re.compile(r'[%s]|$' % + special_char_re = re.compile(r'[%s]|\Z' % u''.join(re.escape(c) for c in special_chars)) escapable_chars = (SYMBOL_DELIM, FUNC_DELIM, GROUP_CLOSE, ARG_SEP) terminator_chars = (GROUP_CLOSE,) @@ -343,8 +343,11 @@ class Parser(object): if self.in_argument: extra_special_chars = (ARG_SEP,) special_char_re = re.compile( - r'[%s]|$' % u''.join(re.escape(c) for c in - self.special_chars + extra_special_chars)) + r'[%s]|\Z' % u''.join( + re.escape(c) for c in + self.special_chars + extra_special_chars + ) + ) text_parts = [] diff --git a/docs/changelog.rst b/docs/changelog.rst index 7edd51cc2..9caca1280 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -106,6 +106,7 @@ Fixes: error message. Thanks to :user:`Mary011196`. :bug:`1676` :bug:`2508` * :doc:`/plugins/web`: Avoid a crash when sending binary data, such as Chromaprint fingerprints, in music attributes. :bug:`2542` :bug:`2532` +* Fix a hang when parsing templates that end in newlines. :bug:`2562` Two plugins had backends removed due to bitrot: diff --git a/test/test_template.py b/test/test_template.py index 1cbe9be0c..288bc2314 100644 --- a/test/test_template.py +++ b/test/test_template.py @@ -227,6 +227,11 @@ class ParseTest(unittest.TestCase): self.assertEqual(parts[2], u',') self._assert_symbol(parts[3], u"bar") + def test_newline_at_end(self): + parts = list(_normparse(u'foo\n')) + self.assertEqual(len(parts), 1) + self.assertEqual(parts[0], u'foo\n') + class EvalTest(unittest.TestCase): def _eval(self, template):