diff --git a/NEWS b/NEWS index 74069fbd9..6bc065ac0 100644 --- a/NEWS +++ b/NEWS @@ -43,6 +43,7 @@ * Fix a "broken pipe" error when piping beets' standard output. * A better error message is given when the database file is unopenable. * Suppress errors due to timeouts and bad responses from MusicBrainz. +* Fix a crash on album queries with item-only field names. 1.0b8 ----- diff --git a/beets/library.py b/beets/library.py index 15ea605f5..30e514046 100644 --- a/beets/library.py +++ b/beets/library.py @@ -411,7 +411,7 @@ class CollectionQuery(Query): return out @classmethod - def from_string(cls, query_string, default_fields=None): + def from_string(cls, query_string, default_fields=None, all_keys=ITEM_KEYS): """Creates a query from a string in the format used by _parse_query. If default_fields are specified, they are the fields to be searched by unqualified search terms. Otherwise, @@ -423,7 +423,7 @@ class CollectionQuery(Query): subqueries.append(AnySubstringQuery(pattern, default_fields)) elif key.lower() == 'comp': # a boolean field subqueries.append(BooleanQuery(key.lower(), pattern)) - elif key.lower() in ITEM_KEYS: # ignore unrecognized keys + elif key.lower() in all_keys: # ignore unrecognized keys subqueries.append(SubstringQuery(key.lower(), pattern)) elif key.lower() == 'singleton': subqueries.append(SingletonQuery(util.str2bool(pattern))) @@ -522,16 +522,22 @@ class BaseLibrary(object): # Helpers. @classmethod - def _get_query(cls, val=None, default_fields=None): + def _get_query(cls, val=None, album=False): """Takes a value which may be None, a query string, or a Query - object, and returns a suitable Query object. If default_fields - is specified, then it restricts the list of fields to search - for unqualified terms in query strings. + object, and returns a suitable Query object. album determines + whether the query is to match items or albums. """ + if album: + default_fields = ALBUM_DEFAULT_FIELDS + all_keys = ALBUM_KEYS + else: + default_fields = ITEM_DEFAULT_FIELDS + all_keys = ITEM_KEYS + if val is None: return TrueQuery() elif isinstance(val, basestring): - return AndQuery.from_string(val, default_fields) + return AndQuery.from_string(val, default_fields, all_keys) elif isinstance(val, Query): return val elif not isinstance(val, Query): @@ -919,7 +925,7 @@ class Library(BaseLibrary): # Querying. def albums(self, query=None, artist=None): - query = self._get_query(query, ALBUM_DEFAULT_FIELDS) + query = self._get_query(query, True) if artist is not None: # "Add" the artist to the query. query = AndQuery((query, MatchQuery('albumartist', artist))) @@ -931,7 +937,7 @@ class Library(BaseLibrary): return [Album(self, dict(res)) for res in c.fetchall()] def items(self, query=None, artist=None, album=None, title=None): - queries = [self._get_query(query, ITEM_DEFAULT_FIELDS)] + queries = [self._get_query(query, False)] if artist is not None: queries.append(MatchQuery('artist', artist)) if album is not None: diff --git a/test/test_query.py b/test/test_query.py index 7f158c19b..7a4f94f24 100644 --- a/test/test_query.py +++ b/test/test_query.py @@ -180,7 +180,7 @@ class MemoryGetTest(unittest.TestCase, AssertsMixin): self.lib = beets.library.Library(':memory:') self.lib.add(self.single_item) - self.lib.add_album([self.album_item]) + self.album = self.lib.add_album([self.album_item]) def test_singleton_true(self): q = 'singleton:true' @@ -206,6 +206,26 @@ class MemoryGetTest(unittest.TestCase, AssertsMixin): self.assert_matched(results, 'singleton item') self.assert_done(results) + def test_unknown_field_name_ignored(self): + q = 'xyzzy:nonsense' + results = self.lib.items(q) + titles = [i.title for i in results] + self.assertTrue('singleton item' in titles) + self.assertTrue('album item' in titles) + self.assertEqual(len(titles), 2) + + def test_unknown_field_name_ignored_in_album_query(self): + q = 'xyzzy:nonsense' + results = self.lib.albums(q) + names = [a.album for a in results] + self.assertEqual(names, ['the album']) + + def test_item_field_name_ignored_in_album_query(self): + q = 'format:nonsense' + results = self.lib.albums(q) + names = [a.album for a in results] + self.assertEqual(names, ['the album']) + class BrowseTest(unittest.TestCase, AssertsMixin): def setUp(self): self.lib = beets.library.Library(