core: Make all methods callable without kwargs, e.g. from Mopidy.js
This commit is contained in:
parent
cc2510bd56
commit
59ccc207ea
@ -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)
|
||||
====================
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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'])
|
||||
|
||||
@ -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')
|
||||
|
||||
|
||||
49
tests/core/tracklist_test.py
Normal file
49
tests/core/tracklist_test.py
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user