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.
|
- 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)
|
v0.10.0 (2012-12-12)
|
||||||
====================
|
====================
|
||||||
|
|||||||
@ -17,23 +17,29 @@ class LibraryController(object):
|
|||||||
uri_scheme = urlparse.urlparse(uri).scheme
|
uri_scheme = urlparse.urlparse(uri).scheme
|
||||||
return self.backends.with_library_by_uri_scheme.get(uri_scheme, None)
|
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``.
|
Search the library for tracks where ``field`` is ``values``.
|
||||||
|
|
||||||
Examples::
|
Examples::
|
||||||
|
|
||||||
# Returns results matching 'a'
|
# Returns results matching 'a'
|
||||||
|
find_exact({'any': ['a']})
|
||||||
find_exact(any=['a'])
|
find_exact(any=['a'])
|
||||||
|
|
||||||
# Returns results matching artist 'xyz'
|
# Returns results matching artist 'xyz'
|
||||||
|
find_exact({'artist': ['xyz']})
|
||||||
find_exact(artist=['xyz'])
|
find_exact(artist=['xyz'])
|
||||||
|
|
||||||
# Returns results matching 'a' and 'b' and 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'])
|
find_exact(any=['a', 'b'], artist=['xyz'])
|
||||||
|
|
||||||
:param query: one or more queries to search for
|
:param query: one or more queries to search for
|
||||||
:type query: dict
|
:type query: dict
|
||||||
:rtype: list of :class:`mopidy.models.Track`
|
:rtype: list of :class:`mopidy.models.Track`
|
||||||
"""
|
"""
|
||||||
|
query = query or kwargs
|
||||||
futures = [
|
futures = [
|
||||||
b.library.find_exact(**query) for b in self.backends.with_library]
|
b.library.find_exact(**query) for b in self.backends.with_library]
|
||||||
results = pykka.get_all(futures)
|
results = pykka.get_all(futures)
|
||||||
@ -72,23 +78,29 @@ class LibraryController(object):
|
|||||||
b.library.refresh(uri) for b in self.backends.with_library]
|
b.library.refresh(uri) for b in self.backends.with_library]
|
||||||
pykka.get_all(futures)
|
pykka.get_all(futures)
|
||||||
|
|
||||||
def search(self, **query):
|
def search(self, query=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Search the library for tracks where ``field`` contains ``values``.
|
Search the library for tracks where ``field`` contains ``values``.
|
||||||
|
|
||||||
Examples::
|
Examples::
|
||||||
|
|
||||||
# Returns results matching 'a'
|
# Returns results matching 'a'
|
||||||
|
search({'any': ['a']})
|
||||||
search(any=['a'])
|
search(any=['a'])
|
||||||
|
|
||||||
# Returns results matching artist 'xyz'
|
# Returns results matching artist 'xyz'
|
||||||
|
search({'artist': ['xyz']})
|
||||||
search(artist=['xyz'])
|
search(artist=['xyz'])
|
||||||
|
|
||||||
# Returns results matching 'a' and 'b' and artist 'xyz'
|
# Returns results matching 'a' and 'b' and artist 'xyz'
|
||||||
|
search({'any': ['a', 'b'], 'artist': ['xyz']})
|
||||||
search(any=['a', 'b'], artist=['xyz'])
|
search(any=['a', 'b'], artist=['xyz'])
|
||||||
|
|
||||||
:param query: one or more queries to search for
|
:param query: one or more queries to search for
|
||||||
:type query: dict
|
:type query: dict
|
||||||
:rtype: list of :class:`mopidy.models.Track`
|
:rtype: list of :class:`mopidy.models.Track`
|
||||||
"""
|
"""
|
||||||
|
query = query or kwargs
|
||||||
futures = [
|
futures = [
|
||||||
b.library.search(**query) for b in self.backends.with_library]
|
b.library.search(**query) for b in self.backends.with_library]
|
||||||
results = pykka.get_all(futures)
|
results = pykka.get_all(futures)
|
||||||
|
|||||||
@ -70,21 +70,29 @@ class PlaylistsController(object):
|
|||||||
if backend:
|
if backend:
|
||||||
backend.playlists.delete(uri).get()
|
backend.playlists.delete(uri).get()
|
||||||
|
|
||||||
def filter(self, **criteria):
|
def filter(self, criteria=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Filter playlists by the given criterias.
|
Filter playlists by the given criterias.
|
||||||
|
|
||||||
Examples::
|
Examples::
|
||||||
|
|
||||||
filter(name='a') # Returns track with name 'a'
|
# Returns track with name 'a'
|
||||||
filter(uri='xyz') # Returns track with URI 'xyz'
|
filter({'name': 'a'})
|
||||||
filter(name='a', uri='xyz') # Returns track with name 'a' and URI
|
filter(name='a')
|
||||||
# 'xyz'
|
|
||||||
|
# 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
|
:param criteria: one or more criteria to match by
|
||||||
:type criteria: dict
|
:type criteria: dict
|
||||||
:rtype: list of :class:`mopidy.models.Playlist`
|
:rtype: list of :class:`mopidy.models.Playlist`
|
||||||
"""
|
"""
|
||||||
|
criteria = criteria or kwargs
|
||||||
matches = self.playlists
|
matches = self.playlists
|
||||||
for (key, value) in criteria.iteritems():
|
for (key, value) in criteria.iteritems():
|
||||||
matches = filter(lambda p: getattr(p, key) == value, matches)
|
matches = filter(lambda p: getattr(p, key) == value, matches)
|
||||||
|
|||||||
@ -103,21 +103,33 @@ class TracklistController(object):
|
|||||||
self._tl_tracks = []
|
self._tl_tracks = []
|
||||||
self._increase_version()
|
self._increase_version()
|
||||||
|
|
||||||
def filter(self, **criteria):
|
def filter(self, criteria=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Filter the tracklist by the given criterias.
|
Filter the tracklist by the given criterias.
|
||||||
|
|
||||||
Examples::
|
Examples::
|
||||||
|
|
||||||
filter(tlid=7) # Returns track with TLID 7 (tracklist ID)
|
# Returns track with TLID 7 (tracklist ID)
|
||||||
filter(id=1) # Returns track with ID 1
|
filter({'tlid': 7})
|
||||||
filter(uri='xyz') # Returns track with URI 'xyz'
|
filter(tlid=7)
|
||||||
filter(id=1, uri='xyz') # Returns track with ID 1 and URI 'xyz'
|
|
||||||
|
# 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
|
:param criteria: on or more criteria to match by
|
||||||
:type criteria: dict
|
:type criteria: dict
|
||||||
:rtype: list of :class:`mopidy.models.TlTrack`
|
:rtype: list of :class:`mopidy.models.TlTrack`
|
||||||
"""
|
"""
|
||||||
|
criteria = criteria or kwargs
|
||||||
matches = self._tl_tracks
|
matches = self._tl_tracks
|
||||||
for (key, value) in criteria.iteritems():
|
for (key, value) in criteria.iteritems():
|
||||||
if key == 'tlid':
|
if key == 'tlid':
|
||||||
@ -172,7 +184,7 @@ class TracklistController(object):
|
|||||||
self._tl_tracks = new_tl_tracks
|
self._tl_tracks = new_tl_tracks
|
||||||
self._increase_version()
|
self._increase_version()
|
||||||
|
|
||||||
def remove(self, **criteria):
|
def remove(self, criteria=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Remove the matching tracks from the tracklist.
|
Remove the matching tracks from the tracklist.
|
||||||
|
|
||||||
@ -184,7 +196,7 @@ class TracklistController(object):
|
|||||||
:type criteria: dict
|
:type criteria: dict
|
||||||
:rtype: list of :class:`mopidy.models.TlTrack` that was removed
|
: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:
|
for tl_track in tl_tracks:
|
||||||
position = self._tl_tracks.index(tl_track)
|
position = self._tl_tracks.index(tl_track)
|
||||||
del self._tl_tracks[position]
|
del self._tl_tracks[position]
|
||||||
|
|||||||
@ -87,6 +87,21 @@ class CoreLibraryTest(unittest.TestCase):
|
|||||||
self.library1.find_exact.assert_called_once_with(any=['a'])
|
self.library1.find_exact.assert_called_once_with(any=['a'])
|
||||||
self.library2.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):
|
def test_search_combines_results_from_all_backends(self):
|
||||||
track1 = Track(uri='dummy1:a')
|
track1 = Track(uri='dummy1:a')
|
||||||
track2 = Track(uri='dummy2:a')
|
track2 = Track(uri='dummy2:a')
|
||||||
@ -101,3 +116,18 @@ class CoreLibraryTest(unittest.TestCase):
|
|||||||
self.assertIn(track2, result)
|
self.assertIn(track2, result)
|
||||||
self.library1.search.assert_called_once_with(any=['a'])
|
self.library1.search.assert_called_once_with(any=['a'])
|
||||||
self.library2.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.has_playlists().get.return_value = False
|
||||||
self.backend3.playlists = None
|
self.backend3.playlists = None
|
||||||
|
|
||||||
self.pl1a = Playlist(tracks=[Track(uri='dummy1:a')])
|
self.pl1a = Playlist(name='A', tracks=[Track(uri='dummy1:a')])
|
||||||
self.pl1b = Playlist(tracks=[Track(uri='dummy1:b')])
|
self.pl1b = Playlist(name='B', tracks=[Track(uri='dummy1:b')])
|
||||||
self.sp1.playlists.get.return_value = [self.pl1a, self.pl1b]
|
self.sp1.playlists.get.return_value = [self.pl1a, self.pl1b]
|
||||||
|
|
||||||
self.pl2a = Playlist(tracks=[Track(uri='dummy2:a')])
|
self.pl2a = Playlist(name='A', tracks=[Track(uri='dummy2:a')])
|
||||||
self.pl2b = Playlist(tracks=[Track(uri='dummy2:b')])
|
self.pl2b = Playlist(name='B', tracks=[Track(uri='dummy2:b')])
|
||||||
self.sp2.playlists.get.return_value = [self.pl2a, self.pl2b]
|
self.sp2.playlists.get.return_value = [self.pl2a, self.pl2b]
|
||||||
|
|
||||||
self.core = Core(audio=None, backends=[
|
self.core = Core(audio=None, backends=[
|
||||||
@ -103,6 +103,16 @@ class PlaylistsTest(unittest.TestCase):
|
|||||||
self.assertFalse(self.sp1.delete.called)
|
self.assertFalse(self.sp1.delete.called)
|
||||||
self.assertFalse(self.sp2.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):
|
def test_lookup_selects_the_dummy1_backend(self):
|
||||||
self.core.playlists.lookup('dummy1:a')
|
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