Merge pull request #377 from jodal/feature/load-extensions

Load backends and frontends from extensions
This commit is contained in:
Thomas Adamcik 2013-04-01 11:03:14 -07:00
commit 605f78065a
16 changed files with 85 additions and 69 deletions

View File

@ -9,6 +9,7 @@ import sys
import gobject
gobject.threads_init()
import pkg_resources
import pykka.debug
@ -54,10 +55,11 @@ def main():
log.setup_logging(options.verbosity_level, options.save_debug_log)
check_old_folders()
setup_settings(options.interactive)
extensions = load_extensions()
audio = setup_audio()
backends = setup_backends(audio)
backends = setup_backends(extensions, audio)
core = setup_core(audio, backends)
setup_frontends(core)
setup_frontends(extensions, core)
loop.run()
except exceptions.SettingsError as ex:
logger.error(ex.message)
@ -67,9 +69,9 @@ def main():
logger.exception(ex)
finally:
loop.quit()
stop_frontends()
stop_frontends(extensions)
stop_core()
stop_backends()
stop_backends(extensions)
stop_audio()
process.stop_remaining_actors()
@ -138,51 +140,86 @@ def setup_settings(interactive):
sys.exit(1)
def load_extensions():
extensions = []
for entry_point in pkg_resources.iter_entry_points('mopidy.extension'):
logger.debug('Loading extension %s', entry_point.name)
try:
extension_class = entry_point.load()
except pkg_resources.DistributionNotFound as ex:
logger.info(
'Disabled extension %s: Dependency %s not found',
entry_point.name, ex)
continue
extension = extension_class()
# TODO Validate configuration, filter out disabled extensions
try:
extension.validate_environment()
except exceptions.ExtensionError as ex:
logger.info(
'Disabled extension: %s (%s)', extension.name, ex.message)
continue
logger.info(
'Loaded extension %s: %s %s',
entry_point.name, extension.name, extension.version)
extensions.append(extension)
return extensions
def setup_audio():
logger.info('Starting Mopidy audio')
return Audio.start().proxy()
def stop_audio():
logger.info('Stopping Mopidy audio')
process.stop_actors_by_class(Audio)
def setup_backends(audio):
def setup_backends(extensions, audio):
logger.info('Starting Mopidy backends')
backends = []
for backend_class_name in settings.BACKENDS:
backend_class = importing.get_class(backend_class_name)
backend = backend_class.start(audio=audio).proxy()
backends.append(backend)
for extension in extensions:
for backend_class in extension.get_backend_classes():
backend = backend_class.start(audio=audio).proxy()
backends.append(backend)
return backends
def stop_backends():
for backend_class_name in settings.BACKENDS:
process.stop_actors_by_class(importing.get_class(backend_class_name))
def stop_backends(extensions):
logger.info('Stopping Mopidy backends')
for extension in extensions:
for backend_class in extension.get_backend_classes():
process.stop_actors_by_class(backend_class)
def setup_core(audio, backends):
logger.info('Starting Mopidy core')
return Core.start(audio=audio, backends=backends).proxy()
def stop_core():
logger.info('Stopping Mopidy core')
process.stop_actors_by_class(Core)
def setup_frontends(core):
for frontend_class_name in settings.FRONTENDS:
try:
importing.get_class(frontend_class_name).start(core=core)
except exceptions.OptionalDependencyError as ex:
logger.info('Disabled: %s (%s)', frontend_class_name, ex)
def setup_frontends(extensions, core):
logger.info('Starting Mopidy frontends')
for extension in extensions:
for frontend_class in extension.get_frontend_classes():
frontend_class.start(core=core)
def stop_frontends():
for frontend_class_name in settings.FRONTENDS:
try:
frontend_class = importing.get_class(frontend_class_name)
def stop_frontends(extensions):
logger.info('Stopping Mopidy frontends')
for extension in extensions:
for frontend_class in extension.get_frontend_classes():
process.stop_actors_by_class(frontend_class)
except exceptions.OptionalDependencyError:
pass
if __name__ == '__main__':

View File

@ -27,10 +27,6 @@ https://github.com/mopidy/mopidy/issues?labels=Local+backend
"""
# TODO Move import into method when BACKENDS setting is removed
from .actor import LocalBackend
class Extension(ext.Extension):
name = 'Mopidy-Local'
@ -46,4 +42,5 @@ class Extension(ext.Extension):
pass
def get_backend_classes(self):
from .actor import LocalBackend
return [LocalBackend]

View File

@ -65,10 +65,6 @@ https://github.com/mopidy/mopidy/issues?labels=Spotify+backend
""" % {'config': indent(config)}
# TODO Move import into method when BACKENDS setting is removed
from .actor import SpotifyBackend
class Extension(ext.Extension):
name = 'Mopidy-Spotify'
@ -92,4 +88,5 @@ class Extension(ext.Extension):
raise ExtensionError('pyspotify library not found', e)
def get_backend_classes(self):
from .actor import SpotifyBackend
return [SpotifyBackend]

View File

@ -24,10 +24,6 @@ https://github.com/mopidy/mopidy/issues?labels=Stream+backend
"""
# TODO Move import into method when BACKENDS setting is removed
from .actor import StreamBackend
class Extension(ext.Extension):
name = 'Mopidy-Stream'
@ -43,4 +39,5 @@ class Extension(ext.Extension):
pass
def get_backend_classes(self):
from .actor import StreamBackend
return [StreamBackend]

View File

@ -485,10 +485,6 @@ Example to get started with
"""
# TODO Move import into method when FRONTENDS setting is removed
from .actor import HttpFrontend
class Extension(ext.Extension):
name = 'Mopidy-HTTP'
@ -512,4 +508,5 @@ class Extension(ext.Extension):
raise ExtensionError('Library ws4py not found', e)
def get_frontend_classes(self):
from .actor import HttpFrontend
return [HttpFrontend]

View File

@ -30,10 +30,6 @@ the Last.fm frontend.
"""
# TODO Move import into method when FRONTENDS setting is removed
from .actor import LastfmFrontend
class Extension(ext.Extension):
name = 'Mopidy-Lastfm'
@ -52,4 +48,5 @@ class Extension(ext.Extension):
raise ExtensionError('pylast library not found', e)
def get_frontend_classes(self):
from .actor import LastfmFrontend
return [LastfmFrontend]

View File

@ -51,10 +51,6 @@ near future:
"""
# TODO Move import into method when FRONTENDS setting is removed
from .actor import MpdFrontend
class Extension(ext.Extension):
name = 'Mopidy-MPD'
@ -70,4 +66,5 @@ class Extension(ext.Extension):
pass
def get_frontend_classes(self):
from .actor import MpdFrontend
return [MpdFrontend]

View File

@ -58,10 +58,6 @@ Now you can control Mopidy through the player object. Examples:
"""
# TODO Move import into method when FRONTENDS setting is removed
from .actor import MprisFrontend
class Extension(ext.Extension):
name = 'Mopidy-MPRIS'
@ -80,4 +76,5 @@ class Extension(ext.Extension):
raise ExtensionError('Library dbus not found', e)
def get_frontend_classes(self):
from .actor import MprisFrontend
return [MprisFrontend]

View File

@ -45,12 +45,12 @@ setup(
'mopidy-scan = mopidy.scanner:main',
],
b'mopidy.extension': [
'http = mopidy.frontends.http:Extension',
'lastfm = mopidy.frontends.lastfm:Extension',
'http = mopidy.frontends.http:Extension [http]',
'lastfm = mopidy.frontends.lastfm:Extension [lastfm]',
'local = mopidy.backends.local:Extension',
'mpd = mopidy.frontends.mpd:Extension',
'mpris = mopidy.frontends.mpris:Extension',
'spotify = mopidy.backends.spotify:Extension',
'spotify = mopidy.backends.spotify:Extension [spotify]',
'stream = mopidy.backends.stream:Extension',
],
},

View File

@ -1,12 +1,12 @@
from mopidy import settings
from mopidy.backends.local import LocalBackend
from mopidy.backends.local import actor
from tests import unittest, path_to_data_dir
from tests.backends.base import events
class LocalBackendEventsTest(events.BackendEventsTest, unittest.TestCase):
backend_class = LocalBackend
backend_class = actor.LocalBackend
def setUp(self):
settings.LOCAL_TAG_CACHE_FILE = path_to_data_dir('empty_tag_cache')

View File

@ -1,7 +1,7 @@
from __future__ import unicode_literals
from mopidy import settings
from mopidy.backends.local import LocalBackend
from mopidy.backends.local import actor
from tests import unittest, path_to_data_dir
from tests.backends.base.library import LibraryControllerTest
@ -9,7 +9,7 @@ from tests.backends.base.library import LibraryControllerTest
class LocalLibraryControllerTest(LibraryControllerTest, unittest.TestCase):
backend_class = LocalBackend
backend_class = actor.LocalBackend
def setUp(self):
settings.LOCAL_TAG_CACHE_FILE = path_to_data_dir('library_tag_cache')

View File

@ -1,7 +1,7 @@
from __future__ import unicode_literals
from mopidy import settings
from mopidy.backends.local import LocalBackend
from mopidy.backends.local import actor
from mopidy.core import PlaybackState
from mopidy.models import Track
from mopidy.utils.path import path_to_uri
@ -12,7 +12,7 @@ from tests.backends.local import generate_song
class LocalPlaybackControllerTest(PlaybackControllerTest, unittest.TestCase):
backend_class = LocalBackend
backend_class = actor.LocalBackend
tracks = [
Track(uri=generate_song(i), length=4464) for i in range(1, 4)]

View File

@ -3,7 +3,7 @@ from __future__ import unicode_literals
import os
from mopidy import settings
from mopidy.backends.local import LocalBackend
from mopidy.backends.local import actor
from mopidy.models import Track
from mopidy.utils.path import path_to_uri
@ -16,7 +16,7 @@ from tests.backends.local import generate_song
class LocalPlaylistsControllerTest(
PlaylistsControllerTest, unittest.TestCase):
backend_class = LocalBackend
backend_class = actor.LocalBackend
def setUp(self):
settings.LOCAL_TAG_CACHE_FILE = path_to_data_dir('empty_tag_cache')

View File

@ -1,7 +1,7 @@
from __future__ import unicode_literals
from mopidy import settings
from mopidy.backends.local import LocalBackend
from mopidy.backends.local import actor
from mopidy.models import Track
from tests import unittest, path_to_data_dir
@ -10,7 +10,7 @@ from tests.backends.local import generate_song
class LocalTracklistControllerTest(TracklistControllerTest, unittest.TestCase):
backend_class = LocalBackend
backend_class = actor.LocalBackend
tracks = [
Track(uri=generate_song(i), length=4464) for i in range(1, 4)]

View File

@ -12,7 +12,7 @@ import mock
from mopidy.exceptions import OptionalDependencyError
try:
from mopidy.frontends.http import HttpFrontend
from mopidy.frontends.http import actor
except OptionalDependencyError:
pass
@ -24,7 +24,7 @@ from tests import unittest
@mock.patch('cherrypy.engine.publish')
class HttpEventsTest(unittest.TestCase):
def setUp(self):
self.http = HttpFrontend(core=mock.Mock())
self.http = actor.HttpFrontend(core=mock.Mock())
def test_track_playback_paused_is_broadcasted(self, publish):
publish.reset_mock()

View File

@ -8,7 +8,7 @@ from mopidy.exceptions import OptionalDependencyError
from mopidy.models import Playlist, TlTrack
try:
from mopidy.frontends.mpris import MprisFrontend, objects
from mopidy.frontends.mpris import actor, objects
except OptionalDependencyError:
pass
@ -19,7 +19,7 @@ from tests import unittest
class BackendEventsTest(unittest.TestCase):
def setUp(self):
# As a plain class, not an actor:
self.mpris_frontend = MprisFrontend(core=None)
self.mpris_frontend = actor.MprisFrontend(core=None)
self.mpris_object = mock.Mock(spec=objects.MprisObject)
self.mpris_frontend.mpris_object = self.mpris_object