Merge pull request #1232 from dprokic/feature/843-core-config-for-data-config-cache-dirs

config: Add config for data/config/cache directories
This commit is contained in:
Stein Magnus Jodal 2015-07-26 12:25:58 +02:00
commit 0dc47e6084
6 changed files with 146 additions and 14 deletions

View File

@ -61,6 +61,18 @@ Mopidy's core has the following configuration values that you can change.
Core configuration
------------------
.. 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.
.. confval:: core/max_tracklist_length
Max length of the tracklist. Defaults to 10000.

View File

@ -16,6 +16,9 @@ from mopidy.internal import path, versioning
logger = logging.getLogger(__name__)
_core_schema = ConfigSchema('core')
_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)

View File

@ -1,4 +1,7 @@
[core]
cache_dir = $XDG_CACHE_DIR/mopidy
config_dir = $XDG_CONFIG_DIR/mopidy
data_dir = $XDG_DATA_DIR/mopidy
max_tracklist_length = 10000
[logging]

View File

@ -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``.

View File

@ -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

View File

@ -1,5 +1,7 @@
from __future__ import absolute_import, unicode_literals
import os
import mock
import pkg_resources
@ -11,7 +13,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 +22,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):
@ -53,8 +55,23 @@ class ExtensionTest(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)
class LoadExtensionsTest(object):
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):
@pytest.yield_fixture
def iter_entry_points_mock(self, request):
@ -70,7 +87,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 +111,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 +130,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 +142,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 +154,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
@ -221,3 +238,36 @@ class ValidateExtensionDataTest(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