From 33cef9c56ea857df916c6613823442b8a6319ba8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dejan=20Proki=C4=87?= Date: Sat, 25 Jul 2015 12:21:57 +0200 Subject: [PATCH 1/6] config: Add config for data/config/cache directories Issue #843 --- docs/config.rst | 12 ++++++++++++ mopidy/config/__init__.py | 3 +++ mopidy/config/default.conf | 3 +++ 3 files changed, 18 insertions(+) diff --git a/docs/config.rst b/docs/config.rst index 3a919b5c..e12c152e 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -68,6 +68,18 @@ Core configuration The original MPD server only supports 10000 tracks in the tracklist. Some MPD clients will crash if this limit is exceeded. +.. confval:: core/cache_dir + + Path to directory for storing cached information. + +.. confval:: core/config_dir + + Path to directory with configuration files. + +.. confval:: core/data_dir + + Path to directory with data files. + Audio configuration ------------------- diff --git a/mopidy/config/__init__.py b/mopidy/config/__init__.py index 13a26412..df5b73bc 100644 --- a/mopidy/config/__init__.py +++ b/mopidy/config/__init__.py @@ -18,6 +18,9 @@ logger = logging.getLogger(__name__) _core_schema = ConfigSchema('core') # MPD supports at most 10k tracks, some clients segfault when this is exceeded. _core_schema['max_tracklist_length'] = Integer(minimum=1, maximum=10000) +_core_schema['cache_dir'] = Path() +_core_schema['config_dir'] = Path() +_core_schema['data_dir'] = Path() _logging_schema = ConfigSchema('logging') _logging_schema['color'] = Boolean() diff --git a/mopidy/config/default.conf b/mopidy/config/default.conf index c214de68..5e749f0c 100644 --- a/mopidy/config/default.conf +++ b/mopidy/config/default.conf @@ -1,5 +1,8 @@ [core] max_tracklist_length = 10000 +cache_dir = $XDG_CACHE_DIR/mopidy +config_dir = $XDG_CONFIG_DIR/mopidy +data_dir = $XDG_DATA_DIR/mopidy [logging] color = true From caff7210558ebb6ed827708aaed6096f12e5d9a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dejan=20Proki=C4=87?= Date: Sat, 25 Jul 2015 15:17:51 +0200 Subject: [PATCH 2/6] config: Reorder core config options As suggested by jodal on pull request #1232 Issue #843 --- docs/config.rst | 14 +++++++------- mopidy/config/__init__.py | 4 ++-- mopidy/config/default.conf | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index e12c152e..b6be6cfa 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -61,13 +61,6 @@ Mopidy's core has the following configuration values that you can change. Core configuration ------------------ -.. confval:: core/max_tracklist_length - - Max length of the tracklist. Defaults to 10000. - - The original MPD server only supports 10000 tracks in the tracklist. Some - MPD clients will crash if this limit is exceeded. - .. confval:: core/cache_dir Path to directory for storing cached information. @@ -80,6 +73,13 @@ Core configuration Path to directory with data files. +.. confval:: core/max_tracklist_length + + Max length of the tracklist. Defaults to 10000. + + The original MPD server only supports 10000 tracks in the tracklist. Some + MPD clients will crash if this limit is exceeded. + Audio configuration ------------------- diff --git a/mopidy/config/__init__.py b/mopidy/config/__init__.py index df5b73bc..561d202a 100644 --- a/mopidy/config/__init__.py +++ b/mopidy/config/__init__.py @@ -16,11 +16,11 @@ from mopidy.internal import path, versioning logger = logging.getLogger(__name__) _core_schema = ConfigSchema('core') -# MPD supports at most 10k tracks, some clients segfault when this is exceeded. -_core_schema['max_tracklist_length'] = Integer(minimum=1, maximum=10000) _core_schema['cache_dir'] = Path() _core_schema['config_dir'] = Path() _core_schema['data_dir'] = Path() +# MPD supports at most 10k tracks, some clients segfault when this is exceeded. +_core_schema['max_tracklist_length'] = Integer(minimum=1, maximum=10000) _logging_schema = ConfigSchema('logging') _logging_schema['color'] = Boolean() diff --git a/mopidy/config/default.conf b/mopidy/config/default.conf index 5e749f0c..675381d9 100644 --- a/mopidy/config/default.conf +++ b/mopidy/config/default.conf @@ -1,8 +1,8 @@ [core] -max_tracklist_length = 10000 cache_dir = $XDG_CACHE_DIR/mopidy config_dir = $XDG_CONFIG_DIR/mopidy data_dir = $XDG_DATA_DIR/mopidy +max_tracklist_length = 10000 [logging] color = true From 1e716c71395d2df5e34fe403115d0dab630f36cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dejan=20Proki=C4=87?= Date: Sat, 25 Jul 2015 16:04:39 +0200 Subject: [PATCH 3/6] tests: Add tests for defaults in core schema config Tests check if default core schema has cache_dir, config_dir, data_dir and max_tracklist_length and if they have proper type --- tests/config/test_defaults.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/config/test_defaults.py diff --git a/tests/config/test_defaults.py b/tests/config/test_defaults.py new file mode 100644 index 00000000..0cf78f6f --- /dev/null +++ b/tests/config/test_defaults.py @@ -0,0 +1,26 @@ +from __future__ import absolute_import, unicode_literals + +from mopidy import config + + +def test_core_schema_has_cache_dir(): + assert 'cache_dir' in config._core_schema + assert isinstance(config._core_schema['cache_dir'], config.Path) + + +def test_core_schema_has_config_dir(): + assert 'config_dir' in config._core_schema + assert isinstance(config._core_schema['config_dir'], config.Path) + + +def test_core_schema_has_data_dir(): + assert 'data_dir' in config._core_schema + assert isinstance(config._core_schema['data_dir'], config.Path) + + +def test_core_schema_has_max_tracklist_length(): + assert 'max_tracklist_length' in config._core_schema + max_tracklist_length_schema = config._core_schema['max_tracklist_length'] + assert isinstance(max_tracklist_length_schema, config.Integer) + assert max_tracklist_length_schema._minimum == 1 + assert max_tracklist_length_schema._maximum == 10000 From 13133fd9190fd467cfd22806ec48411b98b5c705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dejan=20Proki=C4=87?= Date: Sat, 25 Jul 2015 17:17:13 +0200 Subject: [PATCH 4/6] tests: Make test_ext discoverable Classes in this test didn't have prefix Test --- tests/test_ext.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/test_ext.py b/tests/test_ext.py index 748aebb3..b73e2bae 100644 --- a/tests/test_ext.py +++ b/tests/test_ext.py @@ -11,7 +11,7 @@ from mopidy import config, exceptions, ext from tests import IsA, any_unicode -class TestExtension(ext.Extension): +class DummyExtension(ext.Extension): dist_name = 'Mopidy-Foobar' ext_name = 'foobar' version = '1.2.3' @@ -20,10 +20,10 @@ class TestExtension(ext.Extension): return '[foobar]\nenabled = true' -any_testextension = IsA(TestExtension) +any_testextension = IsA(DummyExtension) -class ExtensionTest(object): +class TestExtension(object): @pytest.fixture def extension(self): @@ -54,7 +54,7 @@ class ExtensionTest(object): extension.setup(None) -class LoadExtensionsTest(object): +class TestLoadExtensions(object): @pytest.yield_fixture def iter_entry_points_mock(self, request): @@ -70,7 +70,7 @@ class LoadExtensionsTest(object): def test_load_extensions(self, iter_entry_points_mock): mock_entry_point = mock.Mock() - mock_entry_point.load.return_value = TestExtension + mock_entry_point.load.return_value = DummyExtension iter_entry_points_mock.return_value = [mock_entry_point] @@ -94,7 +94,7 @@ class LoadExtensionsTest(object): def test_gets_instance(self, iter_entry_points_mock): mock_entry_point = mock.Mock() - mock_entry_point.load.return_value = TestExtension() + mock_entry_point.load.return_value = DummyExtension() iter_entry_points_mock.return_value = [mock_entry_point] @@ -113,11 +113,11 @@ class LoadExtensionsTest(object): def test_get_config_schema_fails(self, iter_entry_points_mock): mock_entry_point = mock.Mock() - mock_entry_point.load.return_value = TestExtension + mock_entry_point.load.return_value = DummyExtension iter_entry_points_mock.return_value = [mock_entry_point] - with mock.patch.object(TestExtension, 'get_config_schema') as get: + with mock.patch.object(DummyExtension, 'get_config_schema') as get: get.side_effect = Exception assert ext.load_extensions() == [] @@ -125,11 +125,11 @@ class LoadExtensionsTest(object): def test_get_default_config_fails(self, iter_entry_points_mock): mock_entry_point = mock.Mock() - mock_entry_point.load.return_value = TestExtension + mock_entry_point.load.return_value = DummyExtension iter_entry_points_mock.return_value = [mock_entry_point] - with mock.patch.object(TestExtension, 'get_default_config') as get: + with mock.patch.object(DummyExtension, 'get_default_config') as get: get.side_effect = Exception assert ext.load_extensions() == [] @@ -137,22 +137,22 @@ class LoadExtensionsTest(object): def test_get_command_fails(self, iter_entry_points_mock): mock_entry_point = mock.Mock() - mock_entry_point.load.return_value = TestExtension + mock_entry_point.load.return_value = DummyExtension iter_entry_points_mock.return_value = [mock_entry_point] - with mock.patch.object(TestExtension, 'get_command') as get: + with mock.patch.object(DummyExtension, 'get_command') as get: get.side_effect = Exception assert ext.load_extensions() == [] get.assert_called_once_with() -class ValidateExtensionDataTest(object): +class TestValidateExtensionData(object): @pytest.fixture def ext_data(self): - extension = TestExtension() + extension = DummyExtension() entry_point = mock.Mock() entry_point.name = extension.ext_name From 41db4991aa037e515f071a22b8d830bf1bd21f81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dejan=20Proki=C4=87?= Date: Sat, 25 Jul 2015 18:16:35 +0200 Subject: [PATCH 5/6] ext: Add getter methods for cache, config and data directories Methods do get or create directories --- mopidy/ext.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/mopidy/ext.py b/mopidy/ext.py index ab35008a..cdaa5662 100644 --- a/mopidy/ext.py +++ b/mopidy/ext.py @@ -2,10 +2,12 @@ from __future__ import absolute_import, unicode_literals import collections import logging +import os import pkg_resources from mopidy import config as config_lib, exceptions +from mopidy.internal import path logger = logging.getLogger(__name__) @@ -58,6 +60,42 @@ class Extension(object): schema['enabled'] = config_lib.Boolean() return schema + def get_cache_dir(self, config): + """Get or create cache directory for extension and return path + + :param config: Loaded configuration + :return: string + """ + assert self.ext_name is not None + cache_dir_path = bytes(os.path.join(config['core']['cache_dir'], + self.ext_name)) + path.get_or_create_dir(cache_dir_path) + return cache_dir_path + + def get_config_dir(self, config): + """Get or create configuration directory for extension and return path + + :param config: Loaded configuration + :return: string + """ + assert self.ext_name is not None + config_dir_path = bytes(os.path.join(config['core']['config_dir'], + self.ext_name)) + path.get_or_create_dir(config_dir_path) + return config_dir_path + + def get_data_dir(self, config): + """Get or create data directory for extension and return path + + :param config: Loaded configuration + :returns: string + """ + assert self.ext_name is not None + data_dir_path = bytes(os.path.join(config['core']['data_dir'], + self.ext_name)) + path.get_or_create_dir(data_dir_path) + return data_dir_path + def get_command(self): """Command to expose to command line users running ``mopidy``. From 5f843100cca98167ab350ca49db964c1004ca635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dejan=20Proki=C4=87?= Date: Sat, 25 Jul 2015 18:17:27 +0200 Subject: [PATCH 6/6] tests: Add tests for getter methods for cache, config and data directories --- tests/test_ext.py | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/test_ext.py b/tests/test_ext.py index b73e2bae..1a6bd538 100644 --- a/tests/test_ext.py +++ b/tests/test_ext.py @@ -1,5 +1,7 @@ from __future__ import absolute_import, unicode_literals +import os + import mock import pkg_resources @@ -53,6 +55,21 @@ class TestExtension(object): with pytest.raises(NotImplementedError): extension.setup(None) + def test_get_cache_dir_raises_assertion_error(self, extension): + config = {'core': {'cache_dir': '/tmp'}} + with pytest.raises(AssertionError): # ext_name not set + extension.get_cache_dir(config) + + def test_get_config_dir_raises_assertion_error(self, extension): + config = {'core': {'config_dir': '/tmp'}} + with pytest.raises(AssertionError): # ext_name not set + extension.get_config_dir(config) + + def test_get_data_dir_raises_assertion_error(self, extension): + config = {'core': {'data_dir': '/tmp'}} + with pytest.raises(AssertionError): # ext_name not set + extension.get_data_dir(config) + class TestLoadExtensions(object): @@ -221,3 +238,36 @@ class TestValidateExtensionData(object): def test_no_default_config(self, ext_data): ext_data = ext_data._replace(config_defaults=None) assert not ext.validate_extension_data(ext_data) + + def test_get_cache_dir(self, ext_data): + core_cache_dir = '/tmp' + config = {'core': {'cache_dir': core_cache_dir}} + extension = ext_data.extension + + with mock.patch.object(ext.path, 'get_or_create_dir'): + cache_dir = extension.get_cache_dir(config) + + expected = os.path.join(core_cache_dir, extension.ext_name) + assert cache_dir == expected + + def test_get_config_dir(self, ext_data): + core_config_dir = '/tmp' + config = {'core': {'config_dir': core_config_dir}} + extension = ext_data.extension + + with mock.patch.object(ext.path, 'get_or_create_dir'): + config_dir = extension.get_config_dir(config) + + expected = os.path.join(core_config_dir, extension.ext_name) + assert config_dir == expected + + def test_get_data_dir(self, ext_data): + core_data_dir = '/tmp' + config = {'core': {'data_dir': core_data_dir}} + extension = ext_data.extension + + with mock.patch.object(ext.path, 'get_or_create_dir'): + data_dir = extension.get_data_dir(config) + + expected = os.path.join(core_data_dir, extension.ext_name) + assert data_dir == expected