From ead725e9952670b18559f89e66d91c3de373dee2 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 24 Mar 2015 23:54:49 +0100 Subject: [PATCH] core/backend: Stop supporting old search signatures All backends are expected to support the exact argument. A friendly log message will be printed to prompt users to upgrade backends that fail due to this. --- mopidy/core/library.py | 30 ++++++++----------- mopidy/local/library.py | 9 ++---- tests/core/test_library.py | 61 +++++++++++++++++--------------------- tests/dummy_backend.py | 7 ++--- 4 files changed, 45 insertions(+), 62 deletions(-) diff --git a/mopidy/core/library.py b/mopidy/core/library.py index 44375f58..16e33d33 100644 --- a/mopidy/core/library.py +++ b/mopidy/core/library.py @@ -242,25 +242,21 @@ class LibraryController(object): The ``exact`` keyword argument, which replaces :meth:`find_exact`. """ query = _normalize_query(query or kwargs) - futures = [] + futures = {} for backend, backend_uris in self._get_backends_to_uris(uris).items(): - if hasattr(backend.library, 'find_exact'): - # Backends with find_exact probably don't have support for - # search with the exact kwarg, so give them the legacy calls. - if exact: - futures.append(backend.library.find_exact( - query=query, uris=backend_uris)) - else: - futures.append(backend.library.search( - query=query, uris=backend_uris)) - else: - # Assume backends without find_exact are up to date. Worst case - # the exact gets swallowed by the **kwargs and things hopefully - # still work. - futures.append(backend.library.search( - query=query, uris=backend_uris, exact=exact)) + futures[backend] = backend.library.search( + query=query, uris=backend_uris, exact=exact) - return [result for result in pykka.get_all(futures) if result] + results = [] + for backend, future in futures.items(): + try: + results.append(future.get()) + except TypeError: + backend_name = backend.actor_ref.actor_class.__name__ + logger.warning( + '%s does not implement library.search() with exact ' + 'support. Please upgrade it.', backend_name) + return [r for r in results if r] def _normalize_query(query): diff --git a/mopidy/local/library.py b/mopidy/local/library.py index 77c122bd..5e98964c 100644 --- a/mopidy/local/library.py +++ b/mopidy/local/library.py @@ -51,12 +51,7 @@ class LocalLibraryProvider(backend.LibraryProvider): tracks = [tracks] return tracks - def find_exact(self, query=None, uris=None): + def search(self, query=None, uris=None, exact=False): if not self._library: return None - return self._library.search(query=query, uris=uris, exact=True) - - def search(self, query=None, uris=None): - if not self._library: - return None - return self._library.search(query=query, uris=uris, exact=False) + return self._library.search(query=query, uris=uris, exact=exact) diff --git a/tests/core/test_library.py b/tests/core/test_library.py index 50eb834f..51313daa 100644 --- a/tests/core/test_library.py +++ b/tests/core/test_library.py @@ -361,42 +361,35 @@ class CoreLibraryTest(unittest.TestCase): query={'any': ['foobar']}, uris=None, exact=True) -class LegacyLibraryProvider(backend.LibraryProvider): - def find_exact(self, query=None, uris=None): - pass +class LegacyFindExactToSearchLibraryTest(unittest.TestCase): + def setUp(self): # noqa: N802 + self.backend = mock.Mock() + self.backend.actor_ref.actor_class.__name__ = 'DummyBackend' + self.backend.uri_schemes.get.return_value = ['dummy'] + self.backend.library = mock.Mock(spec=backend.LibraryProvider) + self.core = core.Core(mixer=None, backends=[self.backend]) - -class LegacyCoreLibraryTest(unittest.TestCase): - def test_backend_with_find_exact_gets_find_exact_call(self): - b1 = mock.Mock() - b1.uri_schemes.get.return_value = ['dummy1'] - b1.library = mock.Mock(spec=LegacyLibraryProvider) - - b2 = mock.Mock() - b2.uri_schemes.get.return_value = ['dummy2'] - b2.library = mock.Mock(spec=backend.LibraryProvider) - - c = core.Core(mixer=None, backends=[b1, b2]) - c.library.find_exact(query={'any': ['a']}) - - b1.library.find_exact.assert_called_once_with( - query=dict(any=['a']), uris=None) - b2.library.search.assert_called_once_with( + def test_core_find_exact_calls_backend_search_with_exact(self): + self.core.library.find_exact(query={'any': ['a']}) + self.backend.library.search.assert_called_once_with( query=dict(any=['a']), uris=None, exact=True) - def test_backend_with_find_exact_gets_search_without_exact_arg(self): - b1 = mock.Mock() - b1.uri_schemes.get.return_value = ['dummy1'] - b1.library = mock.Mock(spec=LegacyLibraryProvider) + def test_core_find_exact_handles_legacy_backend(self): + self.backend.library.search.return_value.get.side_effect = TypeError + self.core.library.find_exact(query={'any': ['a']}) + # We are just testing that this doesn't fail. - b2 = mock.Mock() - b2.uri_schemes.get.return_value = ['dummy2'] - b2.library = mock.Mock(spec=backend.LibraryProvider) - - c = core.Core(mixer=None, backends=[b1, b2]) - c.library.search(query={'any': ['a']}) - - b1.library.search.assert_called_once_with( - query=dict(any=['a']), uris=None) - b2.library.search.assert_called_once_with( + def test_core_search_call_backend_search_with_exact(self): + self.core.library.search(query={'any': ['a']}) + self.backend.library.search.assert_called_once_with( query=dict(any=['a']), uris=None, exact=False) + + def test_core_search_with_exact_call_backend_search_with_exact(self): + self.core.library.search(query={'any': ['a']}, exact=True) + self.backend.library.search.assert_called_once_with( + query=dict(any=['a']), uris=None, exact=True) + + def test_core_search_with_handles_legacy_backend(self): + self.backend.library.search.return_value.get.side_effect = TypeError + self.core.library.search(query={'any': ['a']}, exact=True) + # We are just testing that this doesn't fail. diff --git a/tests/dummy_backend.py b/tests/dummy_backend.py index babaf0de..99031ee1 100644 --- a/tests/dummy_backend.py +++ b/tests/dummy_backend.py @@ -46,16 +46,15 @@ class DummyLibraryProvider(backend.LibraryProvider): def get_distinct(self, field, query=None): return self.dummy_get_distinct_result.get(field, set()) - def find_exact(self, **query): - return self.dummy_find_exact_result - def lookup(self, uri): return [t for t in self.dummy_library if uri == t.uri] def refresh(self, uri=None): pass - def search(self, **query): + def search(self, query=None, uris=None, exact=False): + if exact: # TODO: remove uses of dummy_find_exact_result + return self.dummy_find_exact_result return self.dummy_search_result