diff --git a/docs/extensiondev.rst b/docs/extensiondev.rst index b1928798..f2c54847 100644 --- a/docs/extensiondev.rst +++ b/docs/extensiondev.rst @@ -265,13 +265,13 @@ meaningful defaults blank, like ``username`` and ``password``. # You will typically only implement one of the next three methods # in a single extension. - def get_frontend_class(self): + def get_frontend_classes(self): from .frontend import SoundspotFrontend - return SoundspotFrontend + return [SoundspotFrontend] - def get_backend_class(self): + def get_backend_classes(self): from .backend import SoundspotBackend - return SoundspotBackend + return [SoundspotBackend] def register_gstreamer_elements(self): from .mixer import SoundspotMixer diff --git a/mopidy/exceptions.py b/mopidy/exceptions.py index b8d183fb..00c19e9e 100644 --- a/mopidy/exceptions.py +++ b/mopidy/exceptions.py @@ -22,3 +22,7 @@ class SettingsError(MopidyException): class OptionalDependencyError(MopidyException): pass + + +class ExtensionError(MopidyException): + pass diff --git a/mopidy/ext.py b/mopidy/ext.py new file mode 100644 index 00000000..6cc35139 --- /dev/null +++ b/mopidy/ext.py @@ -0,0 +1,27 @@ +from __future__ import unicode_literals + + +class Extension(object): + + name = None + version = None + + def get_default_config(self): + raise NotImplementedError( + 'Add at least a config section with "enabled = true"') + + def validate_config(self, config): + raise NotImplementedError( + 'You must explicitly pass config validation if not needed') + + def validate_environment(self): + pass + + def get_frontend_classes(self): + return [] + + def get_backend_classes(self): + return [] + + def register_gstreamer_elements(self): + pass diff --git a/tests/exceptions_test.py b/tests/exceptions_test.py new file mode 100644 index 00000000..2bc838d7 --- /dev/null +++ b/tests/exceptions_test.py @@ -0,0 +1,25 @@ +from __future__ import unicode_literals + +from mopidy import exceptions + +from tests import unittest + + +class ExceptionsTest(unittest.TestCase): + def test_exception_can_include_message_string(self): + exc = exceptions.MopidyException('foo') + + self.assertEqual(exc.message, 'foo') + self.assertEqual(str(exc), 'foo') + + def test_settings_error_is_a_mopidy_exception(self): + self.assert_(issubclass( + exceptions.SettingsError, exceptions.MopidyException)) + + def test_optional_dependency_error_is_a_mopidy_exception(self): + self.assert_(issubclass( + exceptions.OptionalDependencyError, exceptions.MopidyException)) + + def test_extension_error_is_a_mopidy_exception(self): + self.assert_(issubclass( + exceptions.ExtensionError, exceptions.MopidyException)) diff --git a/tests/ext_test.py b/tests/ext_test.py new file mode 100644 index 00000000..ac238ca5 --- /dev/null +++ b/tests/ext_test.py @@ -0,0 +1,34 @@ +from __future__ import unicode_literals + +from mopidy.ext import Extension + +from tests import unittest + + +class ExtensionTest(unittest.TestCase): + def setUp(self): + self.ext = Extension() + + def test_name_is_none(self): + self.assertIsNone(self.ext.name) + + def test_version_is_none(self): + self.assertIsNone(self.ext.version) + + def test_get_default_config_raises_not_implemented(self): + self.assertRaises(NotImplementedError, self.ext.get_default_config) + + def test_validate_config_raises_not_implemented(self): + self.assertRaises(NotImplementedError, self.ext.validate_config, None) + + def test_validate_environment_does_nothing_by_default(self): + self.assertIsNone(self.ext.validate_environment()) + + def test_get_frontend_classes_returns_an_empty_list(self): + self.assertListEqual(self.ext.get_frontend_classes(), []) + + def test_get_backend_classes_returns_an_empty_list(self): + self.assertListEqual(self.ext.get_backend_classes(), []) + + def test_register_gstreamer_elements_does_nothing_by_default(self): + self.assertIsNone(self.ext.register_gstreamer_elements())