core: Make all methods callable without kwargs, e.g. from Mopidy.js

This commit is contained in:
Stein Magnus Jodal 2012-12-17 15:38:32 +01:00
parent cc2510bd56
commit 59ccc207ea
7 changed files with 151 additions and 18 deletions

View File

@ -33,6 +33,18 @@ v0.11.0 (in development)
- Specified that :attr:`mopidy.models.Playlist.last_modified` should be in UTC.
*Core API:*
- Change the following methods to accept either a dict with filters or kwargs.
Previously they only accepted kwargs, which made them impossible to use from
the Mopidy.js through JSON-RPC, which doesn't support kwargs.
- :meth:`mopidy.core.LibraryController.find_exact`
- :meth:`mopidy.core.LibraryController.search`
- :meth:`mopidy.core.PlaylistsController.filter`
- :meth:`mopidy.core.TracklistController.filter`
- :meth:`mopidy.core.TracklistController.remove`
v0.10.0 (2012-12-12)
====================

View File

@ -17,23 +17,29 @@ class LibraryController(object):
uri_scheme = urlparse.urlparse(uri).scheme
return self.backends.with_library_by_uri_scheme.get(uri_scheme, None)
def find_exact(self, **query):
def find_exact(self, query=None, **kwargs):
"""
Search the library for tracks where ``field`` is ``values``.
Examples::
# Returns results matching 'a'
find_exact({'any': ['a']})
find_exact(any=['a'])
# Returns results matching artist 'xyz'
find_exact({'artist': ['xyz']})
find_exact(artist=['xyz'])
# Returns results matching 'a' and 'b' and artist 'xyz'
find_exact({'any': ['a', 'b'], 'artist': ['xyz']})
find_exact(any=['a', 'b'], artist=['xyz'])
:param query: one or more queries to search for
:type query: dict
:rtype: list of :class:`mopidy.models.Track`
"""
query = query or kwargs
futures = [
b.library.find_exact(**query) for b in self.backends.with_library]
results = pykka.get_all(futures)
@ -72,23 +78,29 @@ class LibraryController(object):
b.library.refresh(uri) for b in self.backends.with_library]
pykka.get_all(futures)
def search(self, **query):
def search(self, query=None, **kwargs):
"""
Search the library for tracks where ``field`` contains ``values``.
Examples::
# Returns results matching 'a'
search({'any': ['a']})
search(any=['a'])
# Returns results matching artist 'xyz'
search({'artist': ['xyz']})
search(artist=['xyz'])
# Returns results matching 'a' and 'b' and artist 'xyz'
search({'any': ['a', 'b'], 'artist': ['xyz']})
search(any=['a', 'b'], artist=['xyz'])
:param query: one or more queries to search for
:type query: dict
:rtype: list of :class:`mopidy.models.Track`
"""
query = query or kwargs
futures = [
b.library.search(**query) for b in self.backends.with_library]
results = pykka.get_all(futures)

View File

@ -70,21 +70,29 @@ class PlaylistsController(object):
if backend:
backend.playlists.delete(uri).get()
def filter(self, **criteria):
def filter(self, criteria=None, **kwargs):
"""
Filter playlists by the given criterias.
Examples::
filter(name='a') # Returns track with name 'a'
filter(uri='xyz') # Returns track with URI 'xyz'
filter(name='a', uri='xyz') # Returns track with name 'a' and URI
# 'xyz'
# Returns track with name 'a'
filter({'name': 'a'})
filter(name='a')
# Returns track with URI 'xyz'
filter({'uri': 'xyz'})
filter(uri='xyz')
# Returns track with name 'a' and URI 'xyz'
filter({'name': 'a', 'uri': 'xyz'})
filter(name='a', uri='xyz')
:param criteria: one or more criteria to match by
:type criteria: dict
:rtype: list of :class:`mopidy.models.Playlist`
"""
criteria = criteria or kwargs
matches = self.playlists
for (key, value) in criteria.iteritems():
matches = filter(lambda p: getattr(p, key) == value, matches)

View File

@ -103,21 +103,33 @@ class TracklistController(object):
self._tl_tracks = []
self._increase_version()
def filter(self, **criteria):
def filter(self, criteria=None, **kwargs):
"""
Filter the tracklist by the given criterias.
Examples::
filter(tlid=7) # Returns track with TLID 7 (tracklist ID)
filter(id=1) # Returns track with ID 1
filter(uri='xyz') # Returns track with URI 'xyz'
filter(id=1, uri='xyz') # Returns track with ID 1 and URI 'xyz'
# Returns track with TLID 7 (tracklist ID)
filter({'tlid': 7})
filter(tlid=7)
# Returns track with ID 1
filter({'id': 1})
filter(id=1)
# Returns track with URI 'xyz'
filter({'uri': 'xyz'})
filter(uri='xyz')
# Returns track with ID 1 and URI 'xyz'
filter({'id': 1, 'uri': 'xyz'})
filter(id=1, uri='xyz')
:param criteria: on or more criteria to match by
:type criteria: dict
:rtype: list of :class:`mopidy.models.TlTrack`
"""
criteria = criteria or kwargs
matches = self._tl_tracks
for (key, value) in criteria.iteritems():
if key == 'tlid':
@ -172,7 +184,7 @@ class TracklistController(object):
self._tl_tracks = new_tl_tracks
self._increase_version()
def remove(self, **criteria):
def remove(self, criteria=None, **kwargs):
"""
Remove the matching tracks from the tracklist.
@ -184,7 +196,7 @@ class TracklistController(object):
:type criteria: dict
:rtype: list of :class:`mopidy.models.TlTrack` that was removed
"""
tl_tracks = self.filter(**criteria)
tl_tracks = self.filter(criteria, **kwargs)
for tl_track in tl_tracks:
position = self._tl_tracks.index(tl_track)
del self._tl_tracks[position]

View File

@ -87,6 +87,21 @@ class CoreLibraryTest(unittest.TestCase):
self.library1.find_exact.assert_called_once_with(any=['a'])
self.library2.find_exact.assert_called_once_with(any=['a'])
def test_find_accepts_query_dict_instead_of_kwargs(self):
track1 = Track(uri='dummy1:a')
track2 = Track(uri='dummy2:a')
self.library1.find_exact().get.return_value = [track1]
self.library1.find_exact.reset_mock()
self.library2.find_exact().get.return_value = [track2]
self.library2.find_exact.reset_mock()
result = self.core.library.find_exact(dict(any=['a']))
self.assertIn(track1, result)
self.assertIn(track2, result)
self.library1.find_exact.assert_called_once_with(any=['a'])
self.library2.find_exact.assert_called_once_with(any=['a'])
def test_search_combines_results_from_all_backends(self):
track1 = Track(uri='dummy1:a')
track2 = Track(uri='dummy2:a')
@ -101,3 +116,18 @@ class CoreLibraryTest(unittest.TestCase):
self.assertIn(track2, result)
self.library1.search.assert_called_once_with(any=['a'])
self.library2.search.assert_called_once_with(any=['a'])
def test_search_accepts_query_dict_instead_of_kwargs(self):
track1 = Track(uri='dummy1:a')
track2 = Track(uri='dummy2:a')
self.library1.search().get.return_value = [track1]
self.library1.search.reset_mock()
self.library2.search().get.return_value = [track2]
self.library2.search.reset_mock()
result = self.core.library.search(dict(any=['a']))
self.assertIn(track1, result)
self.assertIn(track2, result)
self.library1.search.assert_called_once_with(any=['a'])
self.library2.search.assert_called_once_with(any=['a'])

View File

@ -27,12 +27,12 @@ class PlaylistsTest(unittest.TestCase):
self.backend3.has_playlists().get.return_value = False
self.backend3.playlists = None
self.pl1a = Playlist(tracks=[Track(uri='dummy1:a')])
self.pl1b = Playlist(tracks=[Track(uri='dummy1:b')])
self.pl1a = Playlist(name='A', tracks=[Track(uri='dummy1:a')])
self.pl1b = Playlist(name='B', tracks=[Track(uri='dummy1:b')])
self.sp1.playlists.get.return_value = [self.pl1a, self.pl1b]
self.pl2a = Playlist(tracks=[Track(uri='dummy2:a')])
self.pl2b = Playlist(tracks=[Track(uri='dummy2:b')])
self.pl2a = Playlist(name='A', tracks=[Track(uri='dummy2:a')])
self.pl2b = Playlist(name='B', tracks=[Track(uri='dummy2:b')])
self.sp2.playlists.get.return_value = [self.pl2a, self.pl2b]
self.core = Core(audio=None, backends=[
@ -103,6 +103,16 @@ class PlaylistsTest(unittest.TestCase):
self.assertFalse(self.sp1.delete.called)
self.assertFalse(self.sp2.delete.called)
def test_filter_returns_matching_playlists(self):
result = self.core.playlists.filter(name='A')
self.assertEqual(2, len(result))
def test_filter_accepts_dict_instead_of_kwargs(self):
result = self.core.playlists.filter({'name': 'A'})
self.assertEqual(2, len(result))
def test_lookup_selects_the_dummy1_backend(self):
self.core.playlists.lookup('dummy1:a')

View File

@ -0,0 +1,49 @@
from __future__ import unicode_literals
from mopidy.core import Core
from mopidy.models import Track
from tests import unittest
class TracklistTest(unittest.TestCase):
def setUp(self):
self.tracks = [
Track(uri='a', name='foo'),
Track(uri='b', name='foo'),
Track(uri='c', name='bar')
]
self.core = Core(audio=None, backends=[])
self.tl_tracks = self.core.tracklist.add(self.tracks)
def test_remove_removes_tl_tracks_matching_query(self):
tl_tracks = self.core.tracklist.remove(name='foo')
self.assertEqual(2, len(tl_tracks))
self.assertListEqual(self.tl_tracks[:2], tl_tracks)
self.assertEqual(1, self.core.tracklist.length)
self.assertListEqual(self.tl_tracks[2:], self.core.tracklist.tl_tracks)
def test_remove_works_with_dict_instead_of_kwargs(self):
tl_tracks = self.core.tracklist.remove({'name': 'foo'})
self.assertEqual(2, len(tl_tracks))
self.assertListEqual(self.tl_tracks[:2], tl_tracks)
self.assertEqual(1, self.core.tracklist.length)
self.assertListEqual(self.tl_tracks[2:], self.core.tracklist.tl_tracks)
def test_filter_returns_tl_tracks_matching_query(self):
tl_tracks = self.core.tracklist.filter(name='foo')
self.assertEqual(2, len(tl_tracks))
self.assertListEqual(self.tl_tracks[:2], tl_tracks)
def test_filter_works_with_dict_instead_of_kwargs(self):
tl_tracks = self.core.tracklist.filter({'name': 'foo'})
self.assertEqual(2, len(tl_tracks))
self.assertListEqual(self.tl_tracks[:2], tl_tracks)
# TODO Extract tracklist tests from the base backend tests