From b6b542a60fe722f42a1ce97b3ac57da10c1e685e Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 11 Jan 2014 16:51:25 +0100 Subject: [PATCH 1/3] backend: Move backend API to mopidy.backend Keep imports from old locations working until extensions have been updated to use the new location. --- docs/api/backends.rst | 16 +- docs/changelog.rst | 22 +++ mopidy/backend.py | 294 ++++++++++++++++++++++++++++++++++++ mopidy/backends/base.py | 278 ++-------------------------------- mopidy/backends/listener.py | 30 +--- 5 files changed, 343 insertions(+), 297 deletions(-) create mode 100644 mopidy/backend.py diff --git a/docs/api/backends.rst b/docs/api/backends.rst index 40d65f30..fa6d8410 100644 --- a/docs/api/backends.rst +++ b/docs/api/backends.rst @@ -4,46 +4,46 @@ Backend API *********** -.. module:: mopidy.backends.base +.. module:: mopidy.backend :synopsis: The API implemented by backends The backend API is the interface that must be implemented when you create a -backend. If you are working on a frontend and need to access the backend, see -the :ref:`core-api`. +backend. If you are working on a frontend and need to access the backends, see +the :ref:`core-api` instead. Backend class ============= -.. autoclass:: mopidy.backends.base.Backend +.. autoclass:: mopidy.backend.Backend :members: Playback provider ================= -.. autoclass:: mopidy.backends.base.BasePlaybackProvider +.. autoclass:: mopidy.backend.PlaybackProvider :members: Playlists provider ================== -.. autoclass:: mopidy.backends.base.BasePlaylistsProvider +.. autoclass:: mopidy.backend.PlaylistsProvider :members: Library provider ================ -.. autoclass:: mopidy.backends.base.BaseLibraryProvider +.. autoclass:: mopidy.backend.LibraryProvider :members: Backend listener ================ -.. autoclass:: mopidy.backends.listener.BackendListener +.. autoclass:: mopidy.backend.BackendListener :members: diff --git a/docs/changelog.rst b/docs/changelog.rst index 97be243b..fa20f791 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -20,6 +20,28 @@ v0.18.0 (UNRELEASED) virtual file system of tracks. Backends can implement support for this by implementing :meth:`mopidy.backends.base.BaseLibraryController.browse`. +**Backend API** + +- Move the backend API classes from :mod:`mopidy.backends.base` to + :mod:`mopidy.backend` and remove the ``Base`` prefix from the class names: + + - From :class:`mopidy.backends.base.Backend` + to :class:`mopidy.backend.Backend` + + - From :class:`mopidy.backends.base.BaseLibraryProvider` + to :class:`mopidy.backend.LibraryProvider` + + - From :class:`mopidy.backends.base.BasePlaybackProvider` + to :class:`mopidy.backend.PlaybackProvider` + + - From :class:`mopidy.backends.base.BasePlaylistsProvider` + to :class:`mopidy.backend.PlaylistsProvider` + + - From :class:`mopidy.backends.listener.BackendListener` + to :class:`mopidy.backend.BackendListener` + + Imports from the old locations still works, but are deprecated. + **Configuration** - The default for the :option:`mopidy --config` option has been updated to diff --git a/mopidy/backend.py b/mopidy/backend.py new file mode 100644 index 00000000..5db013e0 --- /dev/null +++ b/mopidy/backend.py @@ -0,0 +1,294 @@ +from __future__ import unicode_literals + +import copy + +from mopidy import listener + + +class Backend(object): + #: Actor proxy to an instance of :class:`mopidy.audio.Audio`. + #: + #: Should be passed to the backend constructor as the kwarg ``audio``, + #: which will then set this field. + audio = None + + #: The library provider. An instance of + #: :class:`~mopidy.backend.LibraryProvider`, or :class:`None` if + #: the backend doesn't provide a library. + library = None + + #: The playback provider. An instance of + #: :class:`~mopidy.backend.PlaybackProvider`, or :class:`None` if + #: the backend doesn't provide playback. + playback = None + + #: The playlists provider. An instance of + #: :class:`~mopidy.backend.PlaylistsProvider`, or class:`None` if + #: the backend doesn't provide playlists. + playlists = None + + #: List of URI schemes this backend can handle. + uri_schemes = [] + + # Because the providers is marked as pykka_traversible, we can't get() them + # from another actor, and need helper methods to check if the providers are + # set or None. + + def has_library(self): + return self.library is not None + + def has_playback(self): + return self.playback is not None + + def has_playlists(self): + return self.playlists is not None + + +class LibraryProvider(object): + """ + :param backend: backend the controller is a part of + :type backend: :class:`mopidy.backend.Backend` + """ + + pykka_traversable = True + + root_directory_name = None + """ + Name of the library's root directory in Mopidy's virtual file system. + + *MUST be set by any class that implements :meth:`browse`.* + """ + + def __init__(self, backend): + self.backend = backend + + def browse(self, path): + """ + See :meth:`mopidy.core.LibraryController.browse`. + + If you implement this method, make sure to also set + :attr:`root_directory_name`. + + *MAY be implemented by subclass.* + """ + return [] + + # TODO: replace with search(query, exact=True, ...) + def find_exact(self, query=None, uris=None): + """ + See :meth:`mopidy.core.LibraryController.find_exact`. + + *MAY be implemented by subclass.* + """ + pass + + def lookup(self, uri): + """ + See :meth:`mopidy.core.LibraryController.lookup`. + + *MUST be implemented by subclass.* + """ + raise NotImplementedError + + def refresh(self, uri=None): + """ + See :meth:`mopidy.core.LibraryController.refresh`. + + *MAY be implemented by subclass.* + """ + pass + + def search(self, query=None, uris=None): + """ + See :meth:`mopidy.core.LibraryController.search`. + + *MAY be implemented by subclass.* + """ + pass + + +class PlaybackProvider(object): + """ + :param audio: the audio actor + :type audio: actor proxy to an instance of :class:`mopidy.audio.Audio` + :param backend: the backend + :type backend: :class:`mopidy.backend.Backend` + """ + + pykka_traversable = True + + def __init__(self, audio, backend): + self.audio = audio + self.backend = backend + + def pause(self): + """ + Pause playback. + + *MAY be reimplemented by subclass.* + + :rtype: :class:`True` if successful, else :class:`False` + """ + return self.audio.pause_playback().get() + + def play(self, track): + """ + Play given track. + + *MAY be reimplemented by subclass.* + + :param track: the track to play + :type track: :class:`mopidy.models.Track` + :rtype: :class:`True` if successful, else :class:`False` + """ + self.audio.prepare_change() + self.change_track(track) + return self.audio.start_playback().get() + + def change_track(self, track): + """ + Swith to provided track. + + *MAY be reimplemented by subclass.* + + :param track: the track to play + :type track: :class:`mopidy.models.Track` + :rtype: :class:`True` if successful, else :class:`False` + """ + self.audio.set_uri(track.uri).get() + return True + + def resume(self): + """ + Resume playback at the same time position playback was paused. + + *MAY be reimplemented by subclass.* + + :rtype: :class:`True` if successful, else :class:`False` + """ + return self.audio.start_playback().get() + + def seek(self, time_position): + """ + Seek to a given time position. + + *MAY be reimplemented by subclass.* + + :param time_position: time position in milliseconds + :type time_position: int + :rtype: :class:`True` if successful, else :class:`False` + """ + return self.audio.set_position(time_position).get() + + def stop(self): + """ + Stop playback. + + *MAY be reimplemented by subclass.* + + :rtype: :class:`True` if successful, else :class:`False` + """ + return self.audio.stop_playback().get() + + def get_time_position(self): + """ + Get the current time position in milliseconds. + + *MAY be reimplemented by subclass.* + + :rtype: int + """ + return self.audio.get_position().get() + + +class PlaylistsProvider(object): + """ + :param backend: backend the controller is a part of + :type backend: :class:`mopidy.backend.Backend` instance + """ + + pykka_traversable = True + + def __init__(self, backend): + self.backend = backend + self._playlists = [] + + @property + def playlists(self): + """ + Currently available playlists. + + Read/write. List of :class:`mopidy.models.Playlist`. + """ + return copy.copy(self._playlists) + + @playlists.setter # noqa + def playlists(self, playlists): + self._playlists = playlists + + def create(self, name): + """ + See :meth:`mopidy.core.PlaylistsController.create`. + + *MUST be implemented by subclass.* + """ + raise NotImplementedError + + def delete(self, uri): + """ + See :meth:`mopidy.core.PlaylistsController.delete`. + + *MUST be implemented by subclass.* + """ + raise NotImplementedError + + def lookup(self, uri): + """ + See :meth:`mopidy.core.PlaylistsController.lookup`. + + *MUST be implemented by subclass.* + """ + raise NotImplementedError + + def refresh(self): + """ + See :meth:`mopidy.core.PlaylistsController.refresh`. + + *MUST be implemented by subclass.* + """ + raise NotImplementedError + + def save(self, playlist): + """ + See :meth:`mopidy.core.PlaylistsController.save`. + + *MUST be implemented by subclass.* + """ + raise NotImplementedError + + +class BackendListener(listener.Listener): + """ + Marker interface for recipients of events sent by the backend actors. + + Any Pykka actor that mixes in this class will receive calls to the methods + defined here when the corresponding events happen in the core actor. This + interface is used both for looking up what actors to notify of the events, + and for providing default implementations for those listeners that are not + interested in all events. + + Normally, only the Core actor should mix in this class. + """ + + @staticmethod + def send(event, **kwargs): + """Helper to allow calling of backend listener events""" + listener.send_async(BackendListener, event, **kwargs) + + def playlists_loaded(self): + """ + Called when playlists are loaded or refreshed. + + *MAY* be implemented by actor. + """ + pass diff --git a/mopidy/backends/base.py b/mopidy/backends/base.py index b9c67ad5..aed6ce3e 100644 --- a/mopidy/backends/base.py +++ b/mopidy/backends/base.py @@ -1,265 +1,17 @@ from __future__ import unicode_literals -import copy - - -class Backend(object): - #: Actor proxy to an instance of :class:`mopidy.audio.Audio`. - #: - #: Should be passed to the backend constructor as the kwarg ``audio``, - #: which will then set this field. - audio = None - - #: The library provider. An instance of - #: :class:`~mopidy.backends.base.BaseLibraryProvider`, or :class:`None` if - #: the backend doesn't provide a library. - library = None - - #: The playback provider. An instance of - #: :class:`~mopidy.backends.base.BasePlaybackProvider`, or :class:`None` if - #: the backend doesn't provide playback. - playback = None - - #: The playlists provider. An instance of - #: :class:`~mopidy.backends.base.BasePlaylistsProvider`, or class:`None` if - #: the backend doesn't provide playlists. - playlists = None - - #: List of URI schemes this backend can handle. - uri_schemes = [] - - # Because the providers is marked as pykka_traversible, we can't get() them - # from another actor, and need helper methods to check if the providers are - # set or None. - - def has_library(self): - return self.library is not None - - def has_playback(self): - return self.playback is not None - - def has_playlists(self): - return self.playlists is not None - - -class BaseLibraryProvider(object): - """ - :param backend: backend the controller is a part of - :type backend: :class:`mopidy.backends.base.Backend` - """ - - pykka_traversable = True - - root_directory_name = None - """ - Name of the library's root directory in Mopidy's virtual file system. - - *MUST be set by any class that implements :meth:`browse`.* - """ - - def __init__(self, backend): - self.backend = backend - - def browse(self, path): - """ - See :meth:`mopidy.core.LibraryController.browse`. - - If you implement this method, make sure to also set - :attr:`root_directory_name`. - - *MAY be implemented by subclass.* - """ - return [] - - # TODO: replace with search(query, exact=True, ...) - def find_exact(self, query=None, uris=None): - """ - See :meth:`mopidy.core.LibraryController.find_exact`. - - *MAY be implemented by subclass.* - """ - pass - - def lookup(self, uri): - """ - See :meth:`mopidy.core.LibraryController.lookup`. - - *MUST be implemented by subclass.* - """ - raise NotImplementedError - - def refresh(self, uri=None): - """ - See :meth:`mopidy.core.LibraryController.refresh`. - - *MAY be implemented by subclass.* - """ - pass - - def search(self, query=None, uris=None): - """ - See :meth:`mopidy.core.LibraryController.search`. - - *MAY be implemented by subclass.* - """ - pass - - -class BasePlaybackProvider(object): - """ - :param audio: the audio actor - :type audio: actor proxy to an instance of :class:`mopidy.audio.Audio` - :param backend: the backend - :type backend: :class:`mopidy.backends.base.Backend` - """ - - pykka_traversable = True - - def __init__(self, audio, backend): - self.audio = audio - self.backend = backend - - def pause(self): - """ - Pause playback. - - *MAY be reimplemented by subclass.* - - :rtype: :class:`True` if successful, else :class:`False` - """ - return self.audio.pause_playback().get() - - def play(self, track): - """ - Play given track. - - *MAY be reimplemented by subclass.* - - :param track: the track to play - :type track: :class:`mopidy.models.Track` - :rtype: :class:`True` if successful, else :class:`False` - """ - self.audio.prepare_change() - self.change_track(track) - return self.audio.start_playback().get() - - def change_track(self, track): - """ - Swith to provided track. - - *MAY be reimplemented by subclass.* - - :param track: the track to play - :type track: :class:`mopidy.models.Track` - :rtype: :class:`True` if successful, else :class:`False` - """ - self.audio.set_uri(track.uri).get() - return True - - def resume(self): - """ - Resume playback at the same time position playback was paused. - - *MAY be reimplemented by subclass.* - - :rtype: :class:`True` if successful, else :class:`False` - """ - return self.audio.start_playback().get() - - def seek(self, time_position): - """ - Seek to a given time position. - - *MAY be reimplemented by subclass.* - - :param time_position: time position in milliseconds - :type time_position: int - :rtype: :class:`True` if successful, else :class:`False` - """ - return self.audio.set_position(time_position).get() - - def stop(self): - """ - Stop playback. - - *MAY be reimplemented by subclass.* - - :rtype: :class:`True` if successful, else :class:`False` - """ - return self.audio.stop_playback().get() - - def get_time_position(self): - """ - Get the current time position in milliseconds. - - *MAY be reimplemented by subclass.* - - :rtype: int - """ - return self.audio.get_position().get() - - -class BasePlaylistsProvider(object): - """ - :param backend: backend the controller is a part of - :type backend: :class:`mopidy.backends.base.Backend` - """ - - pykka_traversable = True - - def __init__(self, backend): - self.backend = backend - self._playlists = [] - - @property - def playlists(self): - """ - Currently available playlists. - - Read/write. List of :class:`mopidy.models.Playlist`. - """ - return copy.copy(self._playlists) - - @playlists.setter # noqa - def playlists(self, playlists): - self._playlists = playlists - - def create(self, name): - """ - See :meth:`mopidy.core.PlaylistsController.create`. - - *MUST be implemented by subclass.* - """ - raise NotImplementedError - - def delete(self, uri): - """ - See :meth:`mopidy.core.PlaylistsController.delete`. - - *MUST be implemented by subclass.* - """ - raise NotImplementedError - - def lookup(self, uri): - """ - See :meth:`mopidy.core.PlaylistsController.lookup`. - - *MUST be implemented by subclass.* - """ - raise NotImplementedError - - def refresh(self): - """ - See :meth:`mopidy.core.PlaylistsController.refresh`. - - *MUST be implemented by subclass.* - """ - raise NotImplementedError - - def save(self, playlist): - """ - See :meth:`mopidy.core.PlaylistsController.save`. - - *MUST be implemented by subclass.* - """ - raise NotImplementedError +from mopidy.backend import ( + Backend, + LibraryProvider as BaseLibraryProvider, + PlaybackProvider as BasePlaybackProvider, + PlaylistsProvider as BasePlaylistsProvider) + + +# Make classes previously residing here available in the old location for +# backwards compatibility with extensions targeting Mopidy < 0.18. +__all__ = [ + 'Backend', + 'BaseLibraryProvider', + 'BasePlaybackProvider', + 'BasePlaylistsProvider', +] diff --git a/mopidy/backends/listener.py b/mopidy/backends/listener.py index ee4735e7..0b551f26 100644 --- a/mopidy/backends/listener.py +++ b/mopidy/backends/listener.py @@ -1,30 +1,8 @@ from __future__ import unicode_literals -from mopidy import listener +from mopidy.backend import BackendListener -class BackendListener(listener.Listener): - """ - Marker interface for recipients of events sent by the backend actors. - - Any Pykka actor that mixes in this class will receive calls to the methods - defined here when the corresponding events happen in the core actor. This - interface is used both for looking up what actors to notify of the events, - and for providing default implementations for those listeners that are not - interested in all events. - - Normally, only the Core actor should mix in this class. - """ - - @staticmethod - def send(event, **kwargs): - """Helper to allow calling of backend listener events""" - listener.send_async(BackendListener, event, **kwargs) - - def playlists_loaded(self): - """ - Called when playlists are loaded or refreshed. - - *MAY* be implemented by actor. - """ - pass +# Make classes previously residing here available in the old location for +# backwards compatibility with extensions targeting Mopidy < 0.18. +__all__ = ['BackendListener'] From 05632c3b8bb50ae02e2db51b0f010059b30766be Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 11 Jan 2014 17:52:34 +0100 Subject: [PATCH 2/3] backend: Update backend API imports --- mopidy/core/actor.py | 6 +++--- mopidy/ext.py | 2 +- mopidy/local/actor.py | 4 ++-- mopidy/local/library.py | 4 ++-- mopidy/local/playback.py | 4 ++-- mopidy/local/playlists.py | 6 +++--- mopidy/stream/actor.py | 9 ++++----- tests/{backends => backend}/__init__.py | 0 tests/{backends => backend}/listener_test.py | 4 ++-- tests/core/library_test.py | 9 ++++----- tests/core/playback_test.py | 21 ++++++++++---------- tests/core/playlists_test.py | 9 ++++----- tests/core/tracklist_test.py | 9 ++++----- tests/dummy_backend.py | 10 +++++----- tests/local/events_test.py | 5 ++--- 15 files changed, 48 insertions(+), 54 deletions(-) rename tests/{backends => backend}/__init__.py (100%) rename tests/{backends => backend}/listener_test.py (83%) diff --git a/mopidy/core/actor.py b/mopidy/core/actor.py index 26350f16..0f152436 100644 --- a/mopidy/core/actor.py +++ b/mopidy/core/actor.py @@ -5,8 +5,8 @@ import itertools import pykka -from mopidy.audio import AudioListener, PlaybackState -from mopidy.backends.listener import BackendListener +from mopidy import audio, backend +from mopidy.audio import PlaybackState from mopidy.utils import versioning from .library import LibraryController @@ -16,7 +16,7 @@ from .playlists import PlaylistsController from .tracklist import TracklistController -class Core(pykka.ThreadingActor, AudioListener, BackendListener): +class Core(pykka.ThreadingActor, audio.AudioListener, backend.BackendListener): library = None """The library controller. An instance of :class:`mopidy.core.LibraryController`.""" diff --git a/mopidy/ext.py b/mopidy/ext.py index 58e6caab..a58090cc 100644 --- a/mopidy/ext.py +++ b/mopidy/ext.py @@ -123,7 +123,7 @@ class Extension(object): .. deprecated:: 0.18 Use :meth:`setup` instead. - :returns: list of :class:`~mopidy.backends.base.Backend` subclasses + :returns: list of :class:`~mopidy.backend.Backend` subclasses """ return [] diff --git a/mopidy/local/actor.py b/mopidy/local/actor.py index c29a5dbe..61becc72 100644 --- a/mopidy/local/actor.py +++ b/mopidy/local/actor.py @@ -5,7 +5,7 @@ import os import pykka -from mopidy.backends import base +from mopidy import backend from mopidy.utils import encoding, path from .library import LocalLibraryProvider @@ -15,7 +15,7 @@ from .playlists import LocalPlaylistsProvider logger = logging.getLogger(__name__) -class LocalBackend(pykka.ThreadingActor, base.Backend): +class LocalBackend(pykka.ThreadingActor, backend.Backend): uri_schemes = ['local'] libraries = [] diff --git a/mopidy/local/library.py b/mopidy/local/library.py index 2d0478ab..13d46979 100644 --- a/mopidy/local/library.py +++ b/mopidy/local/library.py @@ -2,12 +2,12 @@ from __future__ import unicode_literals import logging -from mopidy.backends import base +from mopidy import backend logger = logging.getLogger(__name__) -class LocalLibraryProvider(base.BaseLibraryProvider): +class LocalLibraryProvider(backend.LibraryProvider): """Proxy library that delegates work to our active local library.""" root_directory_name = 'local' diff --git a/mopidy/local/playback.py b/mopidy/local/playback.py index 6ef7b410..bd798589 100644 --- a/mopidy/local/playback.py +++ b/mopidy/local/playback.py @@ -2,14 +2,14 @@ from __future__ import unicode_literals import logging -from mopidy.backends import base +from mopidy import backend from . import translator logger = logging.getLogger(__name__) -class LocalPlaybackProvider(base.BasePlaybackProvider): +class LocalPlaybackProvider(backend.PlaybackProvider): def change_track(self, track): track = track.copy(uri=translator.local_track_uri_to_file_uri( track.uri, self.backend.config['local']['media_dir'])) diff --git a/mopidy/local/playlists.py b/mopidy/local/playlists.py index 64019d08..f22c6fde 100644 --- a/mopidy/local/playlists.py +++ b/mopidy/local/playlists.py @@ -5,7 +5,7 @@ import logging import os import shutil -from mopidy.backends import base, listener +from mopidy import backend from mopidy.models import Playlist from mopidy.utils import formatting, path @@ -15,7 +15,7 @@ from .translator import parse_m3u logger = logging.getLogger(__name__) -class LocalPlaylistsProvider(base.BasePlaylistsProvider): +class LocalPlaylistsProvider(backend.PlaylistsProvider): def __init__(self, *args, **kwargs): super(LocalPlaylistsProvider, self).__init__(*args, **kwargs) self._media_dir = self.backend.config['local']['media_dir'] @@ -58,7 +58,7 @@ class LocalPlaylistsProvider(base.BasePlaylistsProvider): self.playlists = playlists # TODO: send what scheme we loaded them for? - listener.BackendListener.send('playlists_loaded') + backend.BackendListener.send('playlists_loaded') logger.info( 'Loaded %d local playlists from %s', diff --git a/mopidy/stream/actor.py b/mopidy/stream/actor.py index a5b2a539..aecc4e42 100644 --- a/mopidy/stream/actor.py +++ b/mopidy/stream/actor.py @@ -5,28 +5,27 @@ import urlparse import pykka -from mopidy import audio as audio_lib, exceptions +from mopidy import audio as audio_lib, backend, exceptions from mopidy.audio import scan -from mopidy.backends import base from mopidy.models import Track logger = logging.getLogger(__name__) -class StreamBackend(pykka.ThreadingActor, base.Backend): +class StreamBackend(pykka.ThreadingActor, backend.Backend): def __init__(self, config, audio): super(StreamBackend, self).__init__() self.library = StreamLibraryProvider( backend=self, timeout=config['stream']['timeout']) - self.playback = base.BasePlaybackProvider(audio=audio, backend=self) + self.playback = backend.PlaybackProvider(audio=audio, backend=self) self.playlists = None self.uri_schemes = audio_lib.supported_uri_schemes( config['stream']['protocols']) -class StreamLibraryProvider(base.BaseLibraryProvider): +class StreamLibraryProvider(backend.LibraryProvider): def __init__(self, backend, timeout): super(StreamLibraryProvider, self).__init__(backend) self._scanner = scan.Scanner(min_duration=None, timeout=timeout) diff --git a/tests/backends/__init__.py b/tests/backend/__init__.py similarity index 100% rename from tests/backends/__init__.py rename to tests/backend/__init__.py diff --git a/tests/backends/listener_test.py b/tests/backend/listener_test.py similarity index 83% rename from tests/backends/listener_test.py rename to tests/backend/listener_test.py index ae2eb997..fd861e4f 100644 --- a/tests/backends/listener_test.py +++ b/tests/backend/listener_test.py @@ -3,12 +3,12 @@ from __future__ import unicode_literals import mock import unittest -from mopidy.backends.listener import BackendListener +from mopidy import backend class BackendListenerTest(unittest.TestCase): def setUp(self): - self.listener = BackendListener() + self.listener = backend.BackendListener() def test_on_event_forwards_to_specific_handler(self): self.listener.playlists_loaded = mock.Mock() diff --git a/tests/core/library_test.py b/tests/core/library_test.py index 44c5e3f1..836a434e 100644 --- a/tests/core/library_test.py +++ b/tests/core/library_test.py @@ -3,8 +3,7 @@ from __future__ import unicode_literals import mock import unittest -from mopidy.backends import base -from mopidy.core import Core +from mopidy import backend, core from mopidy.models import Ref, SearchResult, Track @@ -12,13 +11,13 @@ class CoreLibraryTest(unittest.TestCase): def setUp(self): self.backend1 = mock.Mock() self.backend1.uri_schemes.get.return_value = ['dummy1'] - self.library1 = mock.Mock(spec=base.BaseLibraryProvider) + self.library1 = mock.Mock(spec=backend.LibraryProvider) self.library1.root_directory_name.get.return_value = 'dummy1' self.backend1.library = self.library1 self.backend2 = mock.Mock() self.backend2.uri_schemes.get.return_value = ['dummy2'] - self.library2 = mock.Mock(spec=base.BaseLibraryProvider) + self.library2 = mock.Mock(spec=backend.LibraryProvider) self.library2.root_directory_name.get.return_value = 'dummy2' self.backend2.library = self.library2 @@ -27,7 +26,7 @@ class CoreLibraryTest(unittest.TestCase): self.backend3.uri_schemes.get.return_value = ['dummy3'] self.backend3.has_library().get.return_value = False - self.core = Core(audio=None, backends=[ + self.core = core.Core(audio=None, backends=[ self.backend1, self.backend2, self.backend3]) def test_browse_root_returns_dir_ref_for_each_lib_with_root_dir_name(self): diff --git a/tests/core/playback_test.py b/tests/core/playback_test.py index f3374547..806de40e 100644 --- a/tests/core/playback_test.py +++ b/tests/core/playback_test.py @@ -3,8 +3,7 @@ from __future__ import unicode_literals import mock import unittest -from mopidy.backends import base -from mopidy.core import Core, PlaybackState +from mopidy import backend, core from mopidy.models import Track @@ -12,12 +11,12 @@ class CorePlaybackTest(unittest.TestCase): def setUp(self): self.backend1 = mock.Mock() self.backend1.uri_schemes.get.return_value = ['dummy1'] - self.playback1 = mock.Mock(spec=base.BasePlaybackProvider) + self.playback1 = mock.Mock(spec=backend.PlaybackProvider) self.backend1.playback = self.playback1 self.backend2 = mock.Mock() self.backend2.uri_schemes.get.return_value = ['dummy2'] - self.playback2 = mock.Mock(spec=base.BasePlaybackProvider) + self.playback2 = mock.Mock(spec=backend.PlaybackProvider) self.backend2.playback = self.playback2 # A backend without the optional playback provider @@ -32,7 +31,7 @@ class CorePlaybackTest(unittest.TestCase): Track(uri='dummy1:b', length=40000), ] - self.core = Core(audio=None, backends=[ + self.core = core.Core(audio=None, backends=[ self.backend1, self.backend2, self.backend3]) self.core.tracklist.add(self.tracks) @@ -78,7 +77,7 @@ class CorePlaybackTest(unittest.TestCase): self.core.playback.current_tl_track = self.unplayable_tl_track self.core.playback.pause() - self.assertEqual(self.core.playback.state, PlaybackState.PAUSED) + self.assertEqual(self.core.playback.state, core.PlaybackState.PAUSED) self.assertFalse(self.playback1.pause.called) self.assertFalse(self.playback2.pause.called) @@ -100,10 +99,10 @@ class CorePlaybackTest(unittest.TestCase): def test_resume_does_nothing_if_track_is_unplayable(self): self.core.playback.current_tl_track = self.unplayable_tl_track - self.core.playback.state = PlaybackState.PAUSED + self.core.playback.state = core.PlaybackState.PAUSED self.core.playback.resume() - self.assertEqual(self.core.playback.state, PlaybackState.PAUSED) + self.assertEqual(self.core.playback.state, core.PlaybackState.PAUSED) self.assertFalse(self.playback1.resume.called) self.assertFalse(self.playback2.resume.called) @@ -123,10 +122,10 @@ class CorePlaybackTest(unittest.TestCase): def test_stop_changes_state_even_if_track_is_unplayable(self): self.core.playback.current_tl_track = self.unplayable_tl_track - self.core.playback.state = PlaybackState.PAUSED + self.core.playback.state = core.PlaybackState.PAUSED self.core.playback.stop() - self.assertEqual(self.core.playback.state, PlaybackState.STOPPED) + self.assertEqual(self.core.playback.state, core.PlaybackState.STOPPED) self.assertFalse(self.playback1.stop.called) self.assertFalse(self.playback2.stop.called) @@ -146,7 +145,7 @@ class CorePlaybackTest(unittest.TestCase): def test_seek_fails_for_unplayable_track(self): self.core.playback.current_tl_track = self.unplayable_tl_track - self.core.playback.state = PlaybackState.PLAYING + self.core.playback.state = core.PlaybackState.PLAYING success = self.core.playback.seek(1000) self.assertFalse(success) diff --git a/tests/core/playlists_test.py b/tests/core/playlists_test.py index 01c2b881..ac1787fa 100644 --- a/tests/core/playlists_test.py +++ b/tests/core/playlists_test.py @@ -3,8 +3,7 @@ from __future__ import unicode_literals import mock import unittest -from mopidy.backends import base -from mopidy.core import Core +from mopidy import backend, core from mopidy.models import Playlist, Track @@ -12,12 +11,12 @@ class PlaylistsTest(unittest.TestCase): def setUp(self): self.backend1 = mock.Mock() self.backend1.uri_schemes.get.return_value = ['dummy1'] - self.sp1 = mock.Mock(spec=base.BasePlaylistsProvider) + self.sp1 = mock.Mock(spec=backend.PlaylistsProvider) self.backend1.playlists = self.sp1 self.backend2 = mock.Mock() self.backend2.uri_schemes.get.return_value = ['dummy2'] - self.sp2 = mock.Mock(spec=base.BasePlaylistsProvider) + self.sp2 = mock.Mock(spec=backend.PlaylistsProvider) self.backend2.playlists = self.sp2 # A backend without the optional playlists provider @@ -34,7 +33,7 @@ class PlaylistsTest(unittest.TestCase): 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=[ + self.core = core.Core(audio=None, backends=[ self.backend3, self.backend1, self.backend2]) def test_get_playlists_combines_result_from_backends(self): diff --git a/tests/core/tracklist_test.py b/tests/core/tracklist_test.py index 596a20a6..80b4dd23 100644 --- a/tests/core/tracklist_test.py +++ b/tests/core/tracklist_test.py @@ -3,8 +3,7 @@ from __future__ import unicode_literals import mock import unittest -from mopidy.backends import base -from mopidy.core import Core +from mopidy import backend, core from mopidy.models import Track @@ -18,10 +17,10 @@ class TracklistTest(unittest.TestCase): self.backend = mock.Mock() self.backend.uri_schemes.get.return_value = ['dummy1'] - self.library = mock.Mock(spec=base.BaseLibraryProvider) + self.library = mock.Mock(spec=backend.LibraryProvider) self.backend.library = self.library - self.core = Core(audio=None, backends=[self.backend]) + self.core = core.Core(audio=None, backends=[self.backend]) self.tl_tracks = self.core.tracklist.add(self.tracks) def test_add_by_uri_looks_up_uri_in_library(self): @@ -72,4 +71,4 @@ class TracklistTest(unittest.TestCase): def test_filter_fails_if_values_is_a_string(self): self.assertRaises(ValueError, self.core.tracklist.filter, uri='a') - # TODO Extract tracklist tests from the base backend tests + # TODO Extract tracklist tests from the local backend tests diff --git a/tests/dummy_backend.py b/tests/dummy_backend.py index 9fdedaa6..0b8e3858 100644 --- a/tests/dummy_backend.py +++ b/tests/dummy_backend.py @@ -8,7 +8,7 @@ from __future__ import unicode_literals import pykka -from mopidy.backends import base +from mopidy import backend from mopidy.models import Playlist, SearchResult @@ -16,7 +16,7 @@ def create_dummy_backend_proxy(config=None, audio=None): return DummyBackend.start(config=config, audio=audio).proxy() -class DummyBackend(pykka.ThreadingActor, base.Backend): +class DummyBackend(pykka.ThreadingActor, backend.Backend): def __init__(self, config, audio): super(DummyBackend, self).__init__() @@ -27,7 +27,7 @@ class DummyBackend(pykka.ThreadingActor, base.Backend): self.uri_schemes = ['dummy'] -class DummyLibraryProvider(base.BaseLibraryProvider): +class DummyLibraryProvider(backend.LibraryProvider): root_directory_name = 'dummy' def __init__(self, *args, **kwargs): @@ -53,7 +53,7 @@ class DummyLibraryProvider(base.BaseLibraryProvider): return self.dummy_search_result -class DummyPlaybackProvider(base.BasePlaybackProvider): +class DummyPlaybackProvider(backend.PlaybackProvider): def __init__(self, *args, **kwargs): super(DummyPlaybackProvider, self).__init__(*args, **kwargs) self._time_position = 0 @@ -80,7 +80,7 @@ class DummyPlaybackProvider(base.BasePlaybackProvider): return self._time_position -class DummyPlaylistsProvider(base.BasePlaylistsProvider): +class DummyPlaylistsProvider(backend.PlaylistsProvider): def create(self, name): playlist = Playlist(name=name, uri='dummy:%s' % name) self._playlists.append(playlist) diff --git a/tests/local/events_test.py b/tests/local/events_test.py index 60c0b146..f0fd0959 100644 --- a/tests/local/events_test.py +++ b/tests/local/events_test.py @@ -5,14 +5,13 @@ import unittest import mock import pykka -from mopidy import audio, core -from mopidy.backends import listener +from mopidy import audio, backend, core from mopidy.local import actor from tests import path_to_data_dir -@mock.patch.object(listener.BackendListener, 'send') +@mock.patch.object(backend.BackendListener, 'send') class LocalBackendEventsTest(unittest.TestCase): config = { 'local': { From 81b3f21af0655ae3817644d2a57cda7093b667ac Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 11 Jan 2014 18:28:16 +0100 Subject: [PATCH 3/3] docs: Update backend import --- docs/extensiondev.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/extensiondev.rst b/docs/extensiondev.rst index 8c2cd16c..7368e396 100644 --- a/docs/extensiondev.rst +++ b/docs/extensiondev.rst @@ -344,10 +344,10 @@ passed a reference to the core API when it's created. See the import pykka - from mopidy.core import CoreListener + from mopidy import core - class SoundspotFrontend(pykka.ThreadingActor, CoreListener): + class SoundspotFrontend(pykka.ThreadingActor, core.CoreListener): def __init__(self, core): super(SoundspotFrontend, self).__init__() self.core = core @@ -370,10 +370,10 @@ details. import pykka - from mopidy.backends import base + from mopidy import backend - class SoundspotBackend(pykka.ThreadingActor, base.BaseBackend): + class SoundspotBackend(pykka.ThreadingActor, backend.Backend): def __init__(self, audio): super(SoundspotBackend, self).__init__() self.audio = audio