diff --git a/beetsplug/bucket.py b/beetsplug/bucket.py index c4b886bc1..230cf7fb1 100644 --- a/beetsplug/bucket.py +++ b/beetsplug/bucket.py @@ -20,7 +20,7 @@ import logging import re import string from itertools import tee, izip -from beets import plugins +from beets import plugins, ui log = logging.getLogger('beets') @@ -44,8 +44,8 @@ def span_from_str(span_str): """Convert string to a 4 digits year """ if yearfrom < 100: - raise BucketError("Bucket 'from' year %d must be expressed on 4 " - "digits" % yearfrom) + raise BucketError("%d must be expressed on 4 digits" % yearfrom) + # if two digits only, pick closest year that ends by these two # digits starting from yearfrom if d < 100: @@ -56,7 +56,14 @@ def span_from_str(span_str): return d years = [int(x) for x in re.findall('\d+', span_str)] - years = [normalize_year(x, years[0]) for x in years] + if not years: + raise ui.UserError("invalid range defined for year bucket '%s': no " + "year found" % span_str) + try: + years = [normalize_year(x, years[0]) for x in years] + except BucketError as exc: + raise ui.UserError("invalid range defined for year bucket '%s': %s" % + (span_str, exc)) res = {'from': years[0], 'str': span_str} if len(years) > 1: @@ -153,8 +160,13 @@ def build_alpha_spans(alpha_spans_str): ASCII_DIGITS = string.digits + string.ascii_lowercase for elem in alpha_spans_str: bucket = sorted([x for x in elem.lower() if x.isalnum()]) - beginIdx = ASCII_DIGITS.index(bucket[0]) - endIdx = ASCII_DIGITS.index(bucket[-1]) + if bucket: + beginIdx = ASCII_DIGITS.index(bucket[0]) + endIdx = ASCII_DIGITS.index(bucket[-1]) + else: + raise ui.UserError("invalid range defined for alpha bucket '%s'" + " : no alphanumeric character found" % + elem) spans.append(ASCII_DIGITS[beginIdx:endIdx + 1]) return spans diff --git a/docs/plugins/bucket.rst b/docs/plugins/bucket.rst index ac1291aa8..173d3d147 100644 --- a/docs/plugins/bucket.rst +++ b/docs/plugins/bucket.rst @@ -37,6 +37,6 @@ of declared buckets. bucket_year: ['2000-05'] extrapolate: true -is enough to make the plugin return a five years range for any input year. +is enough to make the plugin return an enclosing five years range for any input year. diff --git a/test/test_bucket.py b/test/test_bucket.py index 2b4d546c8..e6679f3c1 100644 --- a/test/test_bucket.py +++ b/test/test_bucket.py @@ -14,9 +14,10 @@ """Tests for the 'bucket' plugin.""" +from nose.tools import raises from _common import unittest from beetsplug import bucket -from beets import config +from beets import config, ui from helper import TestHelper @@ -95,8 +96,9 @@ class BucketPluginTest(unittest.TestCase, TestHelper): def test_alpha_first_last_chars(self): """Alphabet buckets can be named by listing the 'from-to' syntax""" - self._setup_config(bucket_alpha=['A-D', 'F-H', 'I-Z']) + self._setup_config(bucket_alpha=['0->9','A->D', 'F-H', 'I->Z']) self.assertEqual(self.plugin._tmpl_bucket('garry'), 'F-H') + self.assertEqual(self.plugin._tmpl_bucket('2pac'), '0->9') def test_alpha_out_of_range(self): """If no range match, return the initial""" @@ -105,6 +107,27 @@ class BucketPluginTest(unittest.TestCase, TestHelper): self._setup_config(bucket_alpha=[]) self.assertEqual(self.plugin._tmpl_bucket('errol'), 'E') + @raises(ui.UserError) + def test_bad_alpha_range_def(self): + """If bad alpha range definition, a UserError is raised""" + self._setup_config(bucket_alpha=['$%']) + self.assertEqual(self.plugin._tmpl_bucket('errol'), 'E') + + @raises(ui.UserError) + def test_bad_year_range_def_no4digits(self): + """If bad year range definition, a UserError is raised. + Range origin must be expressed on 4 digits.""" + self._setup_config(bucket_year=['62-64']) + # from year must be expressed on 4 digits + self.assertEqual(self.plugin._tmpl_bucket('1963'), '62-64') + + @raises(ui.UserError) + def test_bad_year_range_def_nodigits(self): + """If bad year range definition, a UserError is raised. + At least the range origin must be declared.""" + self._setup_config(bucket_year=['nodigits']) + self.assertEqual(self.plugin._tmpl_bucket('1963'), '62-64') + def suite(): return unittest.TestLoader().loadTestsFromName(__name__)