diff --git a/docs/settings.rst b/docs/settings.rst
index 4c9acd96..e16cfbfd 100644
--- a/docs/settings.rst
+++ b/docs/settings.rst
@@ -218,6 +218,4 @@ used at the same time without any danger of naming collisions.
Available settings
==================
-.. automodule:: mopidy.settings
- :synopsis: Available settings and their default values
- :members:
+.. note:: TODO: Document config values of the new config system
diff --git a/mopidy/__init__.py b/mopidy/__init__.py
index 416f4fbf..fc2b611b 100644
--- a/mopidy/__init__.py
+++ b/mopidy/__init__.py
@@ -24,8 +24,3 @@ warnings.filterwarnings('ignore', 'could not open display')
__version__ = '0.13.0'
-
-
-from mopidy import settings as default_settings_module
-from mopidy.utils.settings import SettingsProxy
-settings = SettingsProxy(default_settings_module)
diff --git a/mopidy/__main__.py b/mopidy/__main__.py
index 3783d150..94e245d0 100644
--- a/mopidy/__main__.py
+++ b/mopidy/__main__.py
@@ -28,7 +28,7 @@ sys.path.insert(
0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../')))
-from mopidy import exceptions, settings
+from mopidy import exceptions
from mopidy.audio import Audio
from mopidy.config import default_config, config_schemas
from mopidy.core import Core
@@ -48,8 +48,10 @@ def main():
config_files = options.config.split(':')
config_overrides = options.overrides
+ extensions = [] # Make sure it is defined before the finally block
+
try:
- extensions = [] # Make sure it is defined before the finally block
+ create_file_structures()
logging_config = load_config(config_files, config_overrides)
log.setup_logging(
logging_config, options.verbosity_level, options.save_debug_log)
@@ -58,8 +60,7 @@ def main():
extensions = filter_enabled_extensions(raw_config, extensions)
config = validate_config(raw_config, config_schemas, extensions)
log.setup_log_levels(config)
- check_old_folders()
- setup_settings()
+ check_old_locations()
# Anything that wants to exit after this point must use
# mopidy.utils.process.exit_process as actors have been started.
@@ -68,8 +69,6 @@ def main():
core = setup_core(audio, backends)
setup_frontends(config, extensions, core)
loop.run()
- except exceptions.SettingsError as ex:
- logger.error(ex.message)
except KeyboardInterrupt:
logger.info('Interrupted. Exiting...')
except Exception as ex:
@@ -167,17 +166,20 @@ def show_config_callback(option, opt, value, parser):
sys.exit(0)
-def check_old_folders():
- # TODO: add old settings and pre extension storage locations?
- old_settings_folder = os.path.expanduser('~/.mopidy')
+def check_old_locations():
+ dot_mopidy_dir = path.expand_path('~/.mopidy')
+ if os.path.isdir(dot_mopidy_dir):
+ logger.warning(
+ 'Old Mopidy dot dir found at %s. Please migrate your config to '
+ 'the ini-file based config format. See release notes for further '
+ 'instructions.', dot_mopidy_dir)
- if not os.path.isdir(old_settings_folder):
- return
-
- logger.warning(
- 'Old settings folder found at %s, settings.py should be moved '
- 'to %s, any cache data should be deleted. See release notes for '
- 'further instructions.', old_settings_folder, path.SETTINGS_PATH)
+ old_settings_file = path.expand_path('$XDG_CONFIG_DIR/mopidy/settings.py')
+ if os.path.isfile(old_settings_file):
+ logger.warning(
+ 'Old Mopidy settings file found at %s. Please migrate your '
+ 'config to the ini-file based config format. See release notes '
+ 'for further instructions.', old_settings_file)
def load_extensions():
@@ -306,15 +308,10 @@ def validate_config(raw_config, schemas, extensions=None):
return config
-def setup_settings():
- path.get_or_create_folder(path.SETTINGS_PATH)
+def create_file_structures():
path.get_or_create_folder(path.DATA_PATH)
- path.get_or_create_file(path.SETTINGS_FILE)
- try:
- settings.validate()
- except exceptions.SettingsError as ex:
- logger.error(ex.message)
- sys.exit(1)
+ path.get_or_create_folder(path.CONFIG_PATH)
+ path.get_or_create_file(path.CONFIG_FILE)
def setup_audio(config):
diff --git a/mopidy/audio/actor.py b/mopidy/audio/actor.py
index 42dee084..5d92f3c4 100644
--- a/mopidy/audio/actor.py
+++ b/mopidy/audio/actor.py
@@ -9,7 +9,6 @@ import logging
import pykka
-from mopidy import settings
from mopidy.utils import process
from . import mixers, utils
@@ -28,11 +27,14 @@ class Audio(pykka.ThreadingActor):
"""
Audio output through `GStreamer `_.
- **Settings:**
+ **Default config:**
- - :attr:`mopidy.settings.OUTPUT`
- - :attr:`mopidy.settings.MIXER`
- - :attr:`mopidy.settings.MIXER_TRACK`
+ .. code-block:: ini
+
+ [audio]
+ mixer = autoaudiomixer
+ mixer_track =
+ output = autoaudiosink
"""
#: The GStreamer state mapped to :class:`mopidy.audio.PlaybackState`
@@ -41,6 +43,8 @@ class Audio(pykka.ThreadingActor):
def __init__(self, config):
super(Audio, self).__init__()
+ self._config = config
+
self._playbin = None
self._signal_ids = {} # {(element, event): signal_id}
@@ -143,47 +147,51 @@ class Audio(pykka.ThreadingActor):
self._playbin.set_state(gst.STATE_NULL)
def _setup_output(self):
+ output_desc = self._config['audio']['output']
try:
output = gst.parse_bin_from_description(
- settings.OUTPUT, ghost_unconnected_pads=True)
+ output_desc, ghost_unconnected_pads=True)
self._playbin.set_property('audio-sink', output)
- logger.info('Audio output set to "%s"', settings.OUTPUT)
+ logger.info('Audio output set to "%s"', output_desc)
except gobject.GError as ex:
logger.error(
- 'Failed to create audio output "%s": %s', settings.OUTPUT, ex)
+ 'Failed to create audio output "%s": %s', output_desc, ex)
process.exit_process()
def _setup_mixer(self):
- if not settings.MIXER:
+ mixer_desc = self._config['audio']['mixer']
+ track_desc = self._config['audio']['mixer_track']
+
+ if mixer_desc is None:
logger.info('Not setting up audio mixer')
return
- if settings.MIXER == 'software':
+ if mixer_desc == 'software':
self._software_mixing = True
logger.info('Audio mixer is using software mixing')
return
try:
mixerbin = gst.parse_bin_from_description(
- settings.MIXER, ghost_unconnected_pads=False)
+ mixer_desc, ghost_unconnected_pads=False)
except gobject.GError as ex:
logger.warning(
- 'Failed to create audio mixer "%s": %s', settings.MIXER, ex)
+ 'Failed to create audio mixer "%s": %s', mixer_desc, ex)
return
# We assume that the bin will contain a single mixer.
mixer = mixerbin.get_by_interface(b'GstMixer')
if not mixer:
logger.warning(
- 'Did not find any audio mixers in "%s"', settings.MIXER)
+ 'Did not find any audio mixers in "%s"', mixer_desc)
return
if mixerbin.set_state(gst.STATE_READY) != gst.STATE_CHANGE_SUCCESS:
logger.warning(
- 'Setting audio mixer "%s" to READY failed', settings.MIXER)
+ 'Setting audio mixer "%s" to READY failed', mixer_desc)
return
- track = self._select_mixer_track(mixer, settings.MIXER_TRACK)
+ track = self._select_mixer_track(mixer, track_desc)
if not track:
logger.warning('Could not find usable audio mixer track')
return
@@ -198,8 +206,9 @@ class Audio(pykka.ThreadingActor):
def _select_mixer_track(self, mixer, track_label):
# Ignore tracks without volumes, then look for track with
- # label == settings.MIXER_TRACK, otherwise fallback to first usable
- # track hoping the mixer gave them to us in a sensible order.
+ # label equal to the audio/mixer_track config value, otherwise fallback
+ # to first usable track hoping the mixer gave them to us in a sensible
+ # order.
usable_tracks = []
for track in mixer.list_tracks():
diff --git a/mopidy/audio/mixers/auto.py b/mopidy/audio/mixers/auto.py
index 96359da1..b24bcf4c 100644
--- a/mopidy/audio/mixers/auto.py
+++ b/mopidy/audio/mixers/auto.py
@@ -6,9 +6,9 @@ This is Mopidy's default mixer.
None
-**Settings**
+**Configuration**
-If this wasn't the default, you would set :attr:`mopidy.settings.MIXER` to
+If this wasn't the default, you would set the ``audio/mixer`` config value to
``autoaudiomixer`` to use this mixer.
"""
diff --git a/mopidy/audio/mixers/fake.py b/mopidy/audio/mixers/fake.py
index 738491b5..05e86923 100644
--- a/mopidy/audio/mixers/fake.py
+++ b/mopidy/audio/mixers/fake.py
@@ -4,9 +4,9 @@
None
-**Settings**
+**Configuration**
-Set :attr:`mopidy.settings.MIXER` to ``fakemixer`` to use this mixer.
+Set the ``audio/mixer`` config value to ``fakemixer`` to use this mixer.
"""
from __future__ import unicode_literals
diff --git a/mopidy/audio/mixers/nad.py b/mopidy/audio/mixers/nad.py
index 8481de55..058333d1 100644
--- a/mopidy/audio/mixers/nad.py
+++ b/mopidy/audio/mixers/nad.py
@@ -7,10 +7,10 @@ serial cable.
.. literalinclude:: ../../../../requirements/external_mixers.txt
-**Settings**
+**Configuration**
-Set :attr:`mopidy.settings.MIXER` to ``nadmixer`` to use it. You probably also
-needs to add some properties to the ``MIXER`` setting.
+Set the ``audio/mixer`` config value to ``nadmixer`` to use it. You probably
+also needs to add some properties to the ``audio/mixer`` config value.
Supported properties includes:
@@ -34,15 +34,13 @@ Supported properties includes:
Configuration examples::
# Minimum configuration, if the amplifier is available at /dev/ttyUSB0
- MIXER = u'nadmixer'
+ mixer = nadmixer
# Minimum configuration, if the amplifier is available elsewhere
- MIXER = u'nadmixer port=/dev/ttyUSB3'
+ mixer = nadmixer port=/dev/ttyUSB3
# Full configuration
- MIXER = (
- u'nadmixer port=/dev/ttyUSB0 '
- u'source=aux speakers-a=on speakers-b=off')
+ mixer = nadmixer port=/dev/ttyUSB0 source=aux speakers-a=on speakers-b=off
"""
from __future__ import unicode_literals
@@ -132,7 +130,7 @@ class NadTalker(pykka.ThreadingActor):
calibrating the NAD amplifier's volume.
"""
- # Serial link settings
+ # Serial link config
BAUDRATE = 115200
BYTESIZE = 8
PARITY = 'N'
diff --git a/mopidy/backends/local/actor.py b/mopidy/backends/local/actor.py
index abad75ca..a1655dd9 100644
--- a/mopidy/backends/local/actor.py
+++ b/mopidy/backends/local/actor.py
@@ -16,6 +16,8 @@ class LocalBackend(pykka.ThreadingActor, base.Backend):
def __init__(self, config, audio):
super(LocalBackend, self).__init__()
+ self.config = config
+
self.library = LocalLibraryProvider(backend=self)
self.playback = base.BasePlaybackProvider(audio=audio, backend=self)
self.playlists = LocalPlaylistsProvider(backend=self)
diff --git a/mopidy/backends/local/library.py b/mopidy/backends/local/library.py
index f2a1a520..2b1c93f7 100644
--- a/mopidy/backends/local/library.py
+++ b/mopidy/backends/local/library.py
@@ -2,7 +2,6 @@ from __future__ import unicode_literals
import logging
-from mopidy import settings
from mopidy.backends import base
from mopidy.models import Album, SearchResult
@@ -15,15 +14,17 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
def __init__(self, *args, **kwargs):
super(LocalLibraryProvider, self).__init__(*args, **kwargs)
self._uri_mapping = {}
+ self._music_path = self.backend.config['local']['music_path']
+ self._playlist_path = self.backend.config['local']['playlist_path']
+ self._tag_cache_file = self.backend.config['local']['tag_cache_file']
self.refresh()
def refresh(self, uri=None):
- tracks = parse_mpd_tag_cache(
- settings.LOCAL_TAG_CACHE_FILE, settings.LOCAL_MUSIC_PATH)
+ tracks = parse_mpd_tag_cache(self._tag_cache_file, self._music_path)
logger.info(
'Loading tracks from %s using %s',
- settings.LOCAL_MUSIC_PATH, settings.LOCAL_TAG_CACHE_FILE)
+ self._music_path, self._tag_cache_file)
for track in tracks:
self._uri_mapping[track.uri] = track
diff --git a/mopidy/backends/local/playlists.py b/mopidy/backends/local/playlists.py
index 53f7aaae..063d044d 100644
--- a/mopidy/backends/local/playlists.py
+++ b/mopidy/backends/local/playlists.py
@@ -5,7 +5,6 @@ import logging
import os
import shutil
-from mopidy import settings
from mopidy.backends import base, listener
from mopidy.models import Playlist
from mopidy.utils import formatting, path
@@ -19,7 +18,8 @@ logger = logging.getLogger('mopidy.backends.local')
class LocalPlaylistsProvider(base.BasePlaylistsProvider):
def __init__(self, *args, **kwargs):
super(LocalPlaylistsProvider, self).__init__(*args, **kwargs)
- self._path = settings.LOCAL_PLAYLIST_PATH
+ self._music_path = self.backend.config['local']['music_path']
+ self._playlist_path = self.backend.config['local']['playlist_path']
self.refresh()
def create(self, name):
@@ -42,16 +42,16 @@ class LocalPlaylistsProvider(base.BasePlaylistsProvider):
return playlist
def refresh(self):
- logger.info('Loading playlists from %s', self._path)
+ logger.info('Loading playlists from %s', self._playlist_path)
playlists = []
- for m3u in glob.glob(os.path.join(self._path, '*.m3u')):
+ for m3u in glob.glob(os.path.join(self._playlist_path, '*.m3u')):
uri = path.path_to_uri(m3u)
name = os.path.splitext(os.path.basename(m3u))[0]
tracks = []
- for track_uri in parse_m3u(m3u, settings.LOCAL_MUSIC_PATH):
+ for track_uri in parse_m3u(m3u, self._music_path):
try:
# TODO We must use core.library.lookup() to support tracks
# from other backends
@@ -86,13 +86,13 @@ class LocalPlaylistsProvider(base.BasePlaylistsProvider):
def _get_m3u_path(self, name):
name = formatting.slugify(name)
- file_path = os.path.join(self._path, name + '.m3u')
- path.check_file_path_is_inside_base_dir(file_path, self._path)
+ file_path = os.path.join(self._playlist_path, name + '.m3u')
+ path.check_file_path_is_inside_base_dir(file_path, self._playlist_path)
return file_path
def _save_m3u(self, playlist):
file_path = path.uri_to_path(playlist.uri)
- path.check_file_path_is_inside_base_dir(file_path, self._path)
+ path.check_file_path_is_inside_base_dir(file_path, self._playlist_path)
with open(file_path, 'w') as file_handle:
for track in playlist.tracks:
if track.uri.startswith('file://'):
@@ -103,16 +103,18 @@ class LocalPlaylistsProvider(base.BasePlaylistsProvider):
def _delete_m3u(self, uri):
file_path = path.uri_to_path(uri)
- path.check_file_path_is_inside_base_dir(file_path, self._path)
+ path.check_file_path_is_inside_base_dir(file_path, self._playlist_path)
if os.path.exists(file_path):
os.remove(file_path)
def _rename_m3u(self, playlist):
src_file_path = path.uri_to_path(playlist.uri)
- path.check_file_path_is_inside_base_dir(src_file_path, self._path)
+ path.check_file_path_is_inside_base_dir(
+ src_file_path, self._playlist_path)
dst_file_path = self._get_m3u_path(playlist.name)
- path.check_file_path_is_inside_base_dir(dst_file_path, self._path)
+ path.check_file_path_is_inside_base_dir(
+ dst_file_path, self._playlist_path)
shutil.move(src_file_path, dst_file_path)
diff --git a/mopidy/backends/stream/__init__.py b/mopidy/backends/stream/__init__.py
index 9a393bed..17b85d33 100644
--- a/mopidy/backends/stream/__init__.py
+++ b/mopidy/backends/stream/__init__.py
@@ -23,9 +23,8 @@ protocols =
__doc__ = """A backend for playing music for streaming music.
-This backend will handle streaming of URIs in
-:attr:`mopidy.settings.STREAM_PROTOCOLS` assuming the right plugins are
-installed.
+This backend will handle streaming of URIs matching the ``stream/protocols``
+config value, assuming the needed GStreamer plugins are installed.
**Issues**
diff --git a/mopidy/backends/stream/actor.py b/mopidy/backends/stream/actor.py
index d6eb31d3..86df447d 100644
--- a/mopidy/backends/stream/actor.py
+++ b/mopidy/backends/stream/actor.py
@@ -5,7 +5,7 @@ import urlparse
import pykka
-from mopidy import audio as audio_lib, settings
+from mopidy import audio as audio_lib
from mopidy.backends import base
from mopidy.models import Track
@@ -21,7 +21,7 @@ class StreamBackend(pykka.ThreadingActor, base.Backend):
self.playlists = None
self.uri_schemes = audio_lib.supported_uri_schemes(
- settings.STREAM_PROTOCOLS)
+ config['stream']['protocols'])
# TODO: Should we consider letting lookup know how to expand common playlist
diff --git a/mopidy/frontends/http/__init__.py b/mopidy/frontends/http/__init__.py
index 4ca1d9b4..4297378c 100644
--- a/mopidy/frontends/http/__init__.py
+++ b/mopidy/frontends/http/__init__.py
@@ -30,8 +30,7 @@ port = 6680
#
# Change this to have Mopidy serve e.g. files for your JavaScript client.
# "/mopidy" will continue to work as usual even if you change this setting.
-#
-static_path =
+static_dir =
[logging.levels]
cherrypy = warning
@@ -61,19 +60,19 @@ Setup
The frontend is enabled by default if all dependencies are available.
-When it is enabled it starts a web server at the port specified by
-:attr:`mopidy.settings.HTTP_SERVER_PORT`.
+When it is enabled it starts a web server at the port specified by the
+``http/port`` config value.
.. warning:: Security
As a simple security measure, the web server is by default only available
- from localhost. To make it available from other computers, change
- :attr:`mopidy.settings.HTTP_SERVER_HOSTNAME`. Before you do so, note that
- the HTTP frontend does not feature any form of user authentication or
- authorization. Anyone able to access the web server can use the full core
- API of Mopidy. Thus, you probably only want to make the web server
- available from your local network or place it behind a web proxy which
- takes care or user authentication. You have been warned.
+ from localhost. To make it available from other computers, change the
+ ``http/hostname`` config value. Before you do so, note that the HTTP
+ frontend does not feature any form of user authentication or authorization.
+ Anyone able to access the web server can use the full core API of Mopidy.
+ Thus, you probably only want to make the web server available from your
+ local network or place it behind a web proxy which takes care or user
+ authentication. You have been warned.
Using a web based Mopidy client
@@ -81,10 +80,11 @@ Using a web based Mopidy client
The web server can also host any static files, for example the HTML, CSS,
JavaScript, and images needed for a web based Mopidy client. To host static
-files, change :attr:`mopidy.settings.HTTP_SERVER_STATIC_DIR` to point to the
-root directory of your web client, e.g.::
+files, change the ``http/static_dir`` to point to the root directory of your
+web client, e.g.::
- HTTP_SERVER_STATIC_DIR = u'/home/alice/dev/the-client'
+ [http]
+ static_dir = /home/alice/dev/the-client
If the directory includes a file named ``index.html``, it will be served on the
root of Mopidy's web server.
@@ -405,8 +405,7 @@ Example to get started with
2. Create an empty directory for your web client.
-3. Change the setting :attr:`mopidy.settings.HTTP_SERVER_STATIC_DIR` to point
- to your new directory.
+3. Change the ``http/static_dir`` config value to point to your new directory.
4. Start/restart Mopidy.
@@ -533,7 +532,7 @@ class Extension(ext.Extension):
schema = config.ExtensionConfigSchema()
schema['hostname'] = config.Hostname()
schema['port'] = config.Port()
- schema['static_path'] = config.Path(optional=True)
+ schema['static_dir'] = config.Path(optional=True)
return schema
def validate_environment(self):
diff --git a/mopidy/frontends/http/actor.py b/mopidy/frontends/http/actor.py
index 54085471..149cbc7f 100644
--- a/mopidy/frontends/http/actor.py
+++ b/mopidy/frontends/http/actor.py
@@ -6,7 +6,7 @@ import os
import pykka
-from mopidy import exceptions, models, settings
+from mopidy import exceptions, models
from mopidy.core import CoreListener
try:
@@ -25,6 +25,7 @@ logger = logging.getLogger('mopidy.frontends.http')
class HttpFrontend(pykka.ThreadingActor, CoreListener):
def __init__(self, config, core):
super(HttpFrontend, self).__init__()
+ self.config = config
self.core = core
self._setup_server()
self._setup_websocket_plugin()
@@ -35,8 +36,8 @@ class HttpFrontend(pykka.ThreadingActor, CoreListener):
cherrypy.config.update({
'engine.autoreload_on': False,
'server.socket_host': (
- settings.HTTP_SERVER_HOSTNAME.encode('utf-8')),
- 'server.socket_port': settings.HTTP_SERVER_PORT,
+ self.config['http']['hostname'].encode('utf-8')),
+ 'server.socket_port': self.config['http']['port'],
})
def _setup_websocket_plugin(self):
@@ -48,8 +49,8 @@ class HttpFrontend(pykka.ThreadingActor, CoreListener):
root.mopidy = MopidyResource()
root.mopidy.ws = ws.WebSocketResource(self.core)
- if settings.HTTP_SERVER_STATIC_DIR:
- static_dir = settings.HTTP_SERVER_STATIC_DIR
+ if self.config['http']['static_dir']:
+ static_dir = self.config['http']['static_dir']
else:
static_dir = os.path.join(os.path.dirname(__file__), 'data')
logger.debug('HTTP server will serve "%s" at /', static_dir)
diff --git a/mopidy/frontends/mpd/actor.py b/mopidy/frontends/mpd/actor.py
index e288c24e..45ed753e 100644
--- a/mopidy/frontends/mpd/actor.py
+++ b/mopidy/frontends/mpd/actor.py
@@ -5,7 +5,6 @@ import sys
import pykka
-from mopidy import settings
from mopidy.core import CoreListener
from mopidy.frontends.mpd import session
from mopidy.utils import encoding, network, process
@@ -16,17 +15,21 @@ logger = logging.getLogger('mopidy.frontends.mpd')
class MpdFrontend(pykka.ThreadingActor, CoreListener):
def __init__(self, config, core):
super(MpdFrontend, self).__init__()
- hostname = network.format_hostname(settings.MPD_SERVER_HOSTNAME)
- port = settings.MPD_SERVER_PORT
+ hostname = network.format_hostname(config['mpd']['hostname'])
+ port = config['mpd']['port']
# NOTE kwargs dict keys must be bytestrings to work on Python < 2.6.5
# See https://github.com/mopidy/mopidy/issues/302 for details.
try:
network.Server(
hostname, port,
- protocol=session.MpdSession, protocol_kwargs={b'core': core},
- max_connections=settings.MPD_SERVER_MAX_CONNECTIONS,
- timeout=settings.MPD_SERVER_CONNECTION_TIMEOUT)
+ protocol=session.MpdSession,
+ protocol_kwargs={
+ b'config': config,
+ b'core': core,
+ },
+ max_connections=config['mpd']['max_connections'],
+ timeout=config['mpd']['connection_timeout'])
except IOError as error:
logger.error(
'MPD server startup failed: %s',
diff --git a/mopidy/frontends/mpd/dispatcher.py b/mopidy/frontends/mpd/dispatcher.py
index 4f0001ac..dc665abc 100644
--- a/mopidy/frontends/mpd/dispatcher.py
+++ b/mopidy/frontends/mpd/dispatcher.py
@@ -5,7 +5,6 @@ import re
import pykka
-from mopidy import settings
from mopidy.frontends.mpd import exceptions, protocol
logger = logging.getLogger('mopidy.frontends.mpd.dispatcher')
@@ -22,13 +21,15 @@ class MpdDispatcher(object):
_noidle = re.compile(r'^noidle$')
- def __init__(self, session=None, core=None):
+ def __init__(self, session=None, config=None, core=None):
+ self.config = config
self.authenticated = False
self.command_list_receiving = False
self.command_list_ok = False
self.command_list = []
self.command_list_index = None
- self.context = MpdContext(self, session=session, core=core)
+ self.context = MpdContext(
+ self, session=session, config=config, core=core)
def handle_request(self, request, current_command_list_index=None):
"""Dispatch incoming requests to the correct handler."""
@@ -82,7 +83,7 @@ class MpdDispatcher(object):
def _authenticate_filter(self, request, response, filter_chain):
if self.authenticated:
return self._call_next_filter(request, response, filter_chain)
- elif settings.MPD_SERVER_PASSWORD is None:
+ elif self.config['mpd']['password'] is None:
self.authenticated = True
return self._call_next_filter(request, response, filter_chain)
else:
@@ -223,6 +224,9 @@ class MpdContext(object):
#: The current :class:`mopidy.frontends.mpd.MpdSession`.
session = None
+ #: The Mopidy configuration.
+ config = None
+
#: The Mopidy core API. An instance of :class:`mopidy.core.Core`.
core = None
@@ -232,9 +236,10 @@ class MpdContext(object):
#: The subsytems that we want to be notified about in idle mode.
subscriptions = None
- def __init__(self, dispatcher, session=None, core=None):
+ def __init__(self, dispatcher, session=None, config=None, core=None):
self.dispatcher = dispatcher
self.session = session
+ self.config = config
self.core = core
self.events = set()
self.subscriptions = set()
diff --git a/mopidy/frontends/mpd/protocol/connection.py b/mopidy/frontends/mpd/protocol/connection.py
index f7898d21..44696705 100644
--- a/mopidy/frontends/mpd/protocol/connection.py
+++ b/mopidy/frontends/mpd/protocol/connection.py
@@ -1,6 +1,5 @@
from __future__ import unicode_literals
-from mopidy import settings
from mopidy.frontends.mpd.protocol import handle_request
from mopidy.frontends.mpd.exceptions import (
MpdPasswordError, MpdPermissionError)
@@ -40,7 +39,7 @@ def password_(context, password):
This is used for authentication with the server. ``PASSWORD`` is
simply the plaintext password.
"""
- if password == settings.MPD_SERVER_PASSWORD:
+ if password == context.config['mpd']['password']:
context.dispatcher.authenticated = True
else:
raise MpdPasswordError('incorrect password', command='password')
diff --git a/mopidy/frontends/mpd/session.py b/mopidy/frontends/mpd/session.py
index 8a5deecd..14173308 100644
--- a/mopidy/frontends/mpd/session.py
+++ b/mopidy/frontends/mpd/session.py
@@ -18,9 +18,10 @@ class MpdSession(network.LineProtocol):
encoding = protocol.ENCODING
delimiter = r'\r?\n'
- def __init__(self, connection, core=None):
+ def __init__(self, connection, config=None, core=None):
super(MpdSession, self).__init__(connection)
- self.dispatcher = dispatcher.MpdDispatcher(session=self, core=core)
+ self.dispatcher = dispatcher.MpdDispatcher(
+ session=self, config=config, core=core)
def on_start(self):
logger.info('New MPD connection from [%s]:%s', self.host, self.port)
diff --git a/mopidy/frontends/mpd/translator.py b/mopidy/frontends/mpd/translator.py
index 15ca181d..d820b0e0 100644
--- a/mopidy/frontends/mpd/translator.py
+++ b/mopidy/frontends/mpd/translator.py
@@ -5,7 +5,6 @@ import re
import shlex
import urllib
-from mopidy import settings
from mopidy.frontends.mpd import protocol
from mopidy.frontends.mpd.exceptions import MpdArgError
from mopidy.models import TlTrack
@@ -216,12 +215,14 @@ def query_from_mpd_search_format(mpd_query):
return query
-def tracks_to_tag_cache_format(tracks):
+def tracks_to_tag_cache_format(tracks, music_path):
"""
Format list of tracks for output to MPD tag cache
:param tracks: the tracks
:type tracks: list of :class:`mopidy.models.Track`
+ :param music_path: the path to the music dir
+ :type music_path: string
:rtype: list of lists of two-tuples
"""
result = [
@@ -231,14 +232,15 @@ def tracks_to_tag_cache_format(tracks):
('info_end',)
]
tracks.sort(key=lambda t: t.uri)
- _add_to_tag_cache(result, *tracks_to_directory_tree(tracks))
+ folders, files = tracks_to_directory_tree(tracks, music_path)
+ _add_to_tag_cache(result, folders, files, music_path)
return result
-def _add_to_tag_cache(result, folders, files):
- base_path = settings.LOCAL_MUSIC_PATH.encode('utf-8')
+def _add_to_tag_cache(result, folders, files, music_path):
+ base_path = music_path.encode('utf-8')
- for path, entry in folders.items():
+ for path, (entry_folders, entry_files) in folders.items():
try:
text_path = path.decode('utf-8')
except UnicodeDecodeError:
@@ -247,7 +249,7 @@ def _add_to_tag_cache(result, folders, files):
result.append(('directory', text_path))
result.append(('mtime', get_mtime(os.path.join(base_path, path))))
result.append(('begin', name))
- _add_to_tag_cache(result, *entry)
+ _add_to_tag_cache(result, entry_folders, entry_files, music_path)
result.append(('end', name))
result.append(('songList begin',))
@@ -273,7 +275,7 @@ def _add_to_tag_cache(result, folders, files):
result.append(('songList end',))
-def tracks_to_directory_tree(tracks):
+def tracks_to_directory_tree(tracks, music_path):
directories = ({}, [])
for track in tracks:
@@ -282,8 +284,7 @@ def tracks_to_directory_tree(tracks):
absolute_track_dir_path = os.path.dirname(uri_to_path(track.uri))
relative_track_dir_path = re.sub(
- '^' + re.escape(settings.LOCAL_MUSIC_PATH), b'',
- absolute_track_dir_path)
+ '^' + re.escape(music_path), b'', absolute_track_dir_path)
for part in split_path(relative_track_dir_path):
path = os.path.join(path, part)
diff --git a/mopidy/frontends/mpris/__init__.py b/mopidy/frontends/mpris/__init__.py
index 82d15e9d..43061013 100644
--- a/mopidy/frontends/mpris/__init__.py
+++ b/mopidy/frontends/mpris/__init__.py
@@ -32,8 +32,8 @@ An example of an MPRIS client is the `Ubuntu Sound Menu
Ubuntu Sound Menu. The package is named ``python-indicate`` in
Ubuntu/Debian.
-- An ``.desktop`` file for Mopidy installed at the path set in
- :attr:`mopidy.settings.DESKTOP_FILE`. See :ref:`install-desktop-file` for
+- An ``.desktop`` file for Mopidy installed at the path set in the
+ ``mpris/desktop_file`` config value. See :ref:`install-desktop-file` for
details.
**Default config**
diff --git a/mopidy/frontends/mpris/actor.py b/mopidy/frontends/mpris/actor.py
index 11f87922..92805bd3 100644
--- a/mopidy/frontends/mpris/actor.py
+++ b/mopidy/frontends/mpris/actor.py
@@ -4,7 +4,6 @@ import logging
import pykka
-from mopidy import settings
from mopidy.core import CoreListener
from mopidy.frontends.mpris import objects
@@ -20,13 +19,14 @@ except ImportError as import_error:
class MprisFrontend(pykka.ThreadingActor, CoreListener):
def __init__(self, config, core):
super(MprisFrontend, self).__init__()
+ self.config = config
self.core = core
self.indicate_server = None
self.mpris_object = None
def on_start(self):
try:
- self.mpris_object = objects.MprisObject(self.core)
+ self.mpris_object = objects.MprisObject(self.config, self.core)
self._send_startup_notification()
except Exception as e:
logger.error('MPRIS frontend setup failed (%s)', e)
@@ -53,7 +53,8 @@ class MprisFrontend(pykka.ThreadingActor, CoreListener):
logger.debug('Sending startup notification...')
self.indicate_server = indicate.Server()
self.indicate_server.set_type('music.mopidy')
- self.indicate_server.set_desktop_file(settings.DESKTOP_FILE)
+ self.indicate_server.set_desktop_file(
+ self.config['mpris']['desktop_file'])
self.indicate_server.show()
logger.debug('Startup notification sent')
diff --git a/mopidy/frontends/mpris/objects.py b/mopidy/frontends/mpris/objects.py
index 04a72676..696e39bd 100644
--- a/mopidy/frontends/mpris/objects.py
+++ b/mopidy/frontends/mpris/objects.py
@@ -13,7 +13,6 @@ except ImportError as import_error:
from mopidy.exceptions import OptionalDependencyError
raise OptionalDependencyError(import_error)
-from mopidy import settings
from mopidy.core import PlaybackState
from mopidy.utils.process import exit_process
@@ -36,7 +35,8 @@ class MprisObject(dbus.service.Object):
properties = None
- def __init__(self, core):
+ def __init__(self, config, core):
+ self.config = config
self.core = core
self.properties = {
ROOT_IFACE: self._get_root_iface_properties(),
@@ -175,7 +175,8 @@ class MprisObject(dbus.service.Object):
### Root interface properties
def get_DesktopEntry(self):
- return os.path.splitext(os.path.basename(settings.DESKTOP_FILE))[0]
+ return os.path.splitext(os.path.basename(
+ self.config['mpris']['desktop_file']))[0]
def get_SupportedUriSchemes(self):
return dbus.Array(self.core.uri_schemes.get(), signature='s')
diff --git a/mopidy/frontends/scrobbler/actor.py b/mopidy/frontends/scrobbler/actor.py
index eea088de..1809661a 100644
--- a/mopidy/frontends/scrobbler/actor.py
+++ b/mopidy/frontends/scrobbler/actor.py
@@ -5,7 +5,7 @@ import time
import pykka
-from mopidy import exceptions, settings
+from mopidy import exceptions
from mopidy.core import CoreListener
try:
@@ -22,21 +22,17 @@ API_SECRET = '94d9a09c0cd5be955c4afaeaffcaefcd'
class ScrobblerFrontend(pykka.ThreadingActor, CoreListener):
def __init__(self, config, core):
super(ScrobblerFrontend, self).__init__()
+ self.config = config
self.lastfm = None
self.last_start_time = None
def on_start(self):
try:
- username = settings.LASTFM_USERNAME
- password_hash = pylast.md5(settings.LASTFM_PASSWORD)
self.lastfm = pylast.LastFMNetwork(
api_key=API_KEY, api_secret=API_SECRET,
- username=username, password_hash=password_hash)
+ username=self.config['scrobbler']['username'],
+ password_hash=pylast.md5(self.config['scrobbler']['password']))
logger.info('Connected to Last.fm')
- except exceptions.SettingsError as e:
- logger.info('Last.fm scrobbler not started')
- logger.debug('Last.fm settings error: %s', e)
- self.stop()
except (pylast.NetworkError, pylast.MalformedResponseError,
pylast.WSError) as e:
logger.error('Error during Last.fm setup: %s', e)
diff --git a/mopidy/scanner.py b/mopidy/scanner.py
index 4b057774..364828f4 100644
--- a/mopidy/scanner.py
+++ b/mopidy/scanner.py
@@ -34,7 +34,6 @@ import pygst
pygst.require('0.10')
import gst
-from mopidy import settings
from mopidy.frontends.mpd import translator as mpd_translator
from mopidy.models import Track, Artist, Album
from mopidy.utils import log, path, versioning
@@ -42,6 +41,7 @@ from mopidy.utils import log, path, versioning
def main():
options = parse_options()
+ config = {} # TODO Read config from new config system
log.setup_root_logger()
log.setup_console_logging(options.verbosity_level)
@@ -57,9 +57,9 @@ def main():
logging.warning('Failed %s: %s', uri, error)
logging.debug('Debug info for %s: %s', uri, debug)
- logging.info('Scanning %s', settings.LOCAL_MUSIC_PATH)
+ logging.info('Scanning %s', config['local']['music_path'])
- scanner = Scanner(settings.LOCAL_MUSIC_PATH, store, debug)
+ scanner = Scanner(config['local']['music_path'], store, debug)
try:
scanner.start()
except KeyboardInterrupt:
@@ -67,7 +67,8 @@ def main():
logging.info('Done scanning; writing tag cache...')
- for row in mpd_translator.tracks_to_tag_cache_format(tracks):
+ for row in mpd_translator.tracks_to_tag_cache_format(
+ tracks, config['mpd']['music_path']):
if len(row) == 1:
print ('%s' % row).encode('utf-8')
else:
diff --git a/mopidy/settings.py b/mopidy/settings.py
deleted file mode 100644
index cde6430a..00000000
--- a/mopidy/settings.py
+++ /dev/null
@@ -1,285 +0,0 @@
-"""
-All available settings and their default values.
-
-.. warning::
-
- Do *not* change settings directly in :mod:`mopidy.settings`. Instead, add a
- file called ``~/.config/mopidy/settings.py`` and redefine settings there.
-"""
-
-from __future__ import unicode_literals
-
-#: The log format used for informational logging.
-#:
-#: See http://docs.python.org/2/library/logging.html#formatter-objects for
-#: details on the format.
-CONSOLE_LOG_FORMAT = '%(levelname)-8s %(message)s'
-
-#: The log format used for debug logging.
-#:
-#: See http://docs.python.org/library/logging.html#formatter-objects for
-#: details on the format.
-DEBUG_LOG_FORMAT = '%(levelname)-8s %(asctime)s' + \
- ' [%(process)d:%(threadName)s] %(name)s\n %(message)s'
-
-#: The file to dump debug log data to when Mopidy is run with the
-#: :option:`--save-debug-log` option.
-#:
-#: Default::
-#:
-#: DEBUG_LOG_FILENAME = u'mopidy.log'
-DEBUG_LOG_FILENAME = 'mopidy.log'
-
-#: Location of the Mopidy .desktop file.
-#:
-#: Used by :mod:`mopidy.frontends.mpris`.
-#:
-#: Default::
-#:
-#: DESKTOP_FILE = u'/usr/share/applications/mopidy.desktop'
-DESKTOP_FILE = '/usr/share/applications/mopidy.desktop'
-
-#: Which address Mopidy's HTTP server should bind to.
-#:
-#: Used by :mod:`mopidy.frontends.http`.
-#:
-#: Examples:
-#:
-#: ``127.0.0.1``
-#: Listens only on the IPv4 loopback interface. Default.
-#: ``::1``
-#: Listens only on the IPv6 loopback interface.
-#: ``0.0.0.0``
-#: Listens on all IPv4 interfaces.
-#: ``::``
-#: Listens on all interfaces, both IPv4 and IPv6.
-HTTP_SERVER_HOSTNAME = u'127.0.0.1'
-
-#: Which TCP port Mopidy's HTTP server should listen to.
-#:
-#: Used by :mod:`mopidy.frontends.http`.
-#:
-#: Default: 6680
-HTTP_SERVER_PORT = 6680
-
-#: Which directory Mopidy's HTTP server should serve at ``/``.
-#:
-#: Change this to have Mopidy serve e.g. files for your JavaScript client.
-#: ``/mopidy`` will continue to work as usual even if you change this setting.
-#:
-#: Used by :mod:`mopidy.frontends.http`.
-#:
-#: Default: None
-HTTP_SERVER_STATIC_DIR = None
-
-#: Your `Last.fm `_ username.
-#:
-#: Used by :mod:`mopidy.frontends.lastfm`.
-LASTFM_USERNAME = ''
-
-#: Your `Last.fm `_ password.
-#:
-#: Used by :mod:`mopidy.frontends.lastfm`.
-LASTFM_PASSWORD = ''
-
-#: Path to folder with local music.
-#:
-#: Used by :mod:`mopidy.backends.local`.
-#:
-#: Default::
-#:
-#: LOCAL_MUSIC_PATH = u'$XDG_MUSIC_DIR'
-LOCAL_MUSIC_PATH = '$XDG_MUSIC_DIR'
-
-#: Path to playlist folder with m3u files for local music.
-#:
-#: Used by :mod:`mopidy.backends.local`.
-#:
-#: Default::
-#:
-#: LOCAL_PLAYLIST_PATH = u'$XDG_DATA_DIR/mopidy/playlists'
-LOCAL_PLAYLIST_PATH = '$XDG_DATA_DIR/mopidy/playlists'
-
-#: Path to tag cache for local music.
-#:
-#: Used by :mod:`mopidy.backends.local`.
-#:
-#: Default::
-#:
-#: LOCAL_TAG_CACHE_FILE = u'$XDG_DATA_DIR/mopidy/tag_cache'
-LOCAL_TAG_CACHE_FILE = '$XDG_DATA_DIR/mopidy/tag_cache'
-
-#: Audio mixer to use.
-#:
-#: Expects a GStreamer mixer to use, typical values are:
-#: ``alsamixer``, ``pulsemixer``, ``ossmixer``, and ``oss4mixer``.
-#:
-#: Setting this to :class:`None` turns off volume control. ``software``
-#: can be used to force software mixing in the application.
-#:
-#: Default::
-#:
-#: MIXER = u'autoaudiomixer'
-MIXER = 'autoaudiomixer'
-
-#: Audio mixer track to use.
-#:
-#: Name of the mixer track to use. If this is not set we will try to find the
-#: master output track. As an example, using ``alsamixer`` you would
-#: typically set this to ``Master`` or ``PCM``.
-#:
-#: Default::
-#:
-#: MIXER_TRACK = None
-MIXER_TRACK = None
-
-#: Number of seconds an MPD client can stay inactive before the connection is
-#: closed by the server.
-#:
-#: Used by :mod:`mopidy.frontends.mpd`.
-#:
-#: Default::
-#:
-#: MPD_SERVER_CONNECTION_TIMEOUT = 60
-MPD_SERVER_CONNECTION_TIMEOUT = 60
-
-#: Which address Mopidy's MPD server should bind to.
-#:
-#: Used by :mod:`mopidy.frontends.mpd`.
-#:
-#: Examples:
-#:
-#: ``127.0.0.1``
-#: Listens only on the IPv4 loopback interface. Default.
-#: ``::1``
-#: Listens only on the IPv6 loopback interface.
-#: ``0.0.0.0``
-#: Listens on all IPv4 interfaces.
-#: ``::``
-#: Listens on all interfaces, both IPv4 and IPv6.
-MPD_SERVER_HOSTNAME = '127.0.0.1'
-
-#: Which TCP port Mopidy's MPD server should listen to.
-#:
-#: Used by :mod:`mopidy.frontends.mpd`.
-#:
-#: Default: 6600
-MPD_SERVER_PORT = 6600
-
-#: The password required for connecting to the MPD server.
-#:
-#: Used by :mod:`mopidy.frontends.mpd`.
-#:
-#: Default: :class:`None`, which means no password required.
-MPD_SERVER_PASSWORD = None
-
-#: The maximum number of concurrent connections the MPD server will accept.
-#:
-#: Used by :mod:`mopidy.frontends.mpd`.
-#:
-#: Default: 20
-MPD_SERVER_MAX_CONNECTIONS = 20
-
-#: Audio output to use.
-#:
-#: Expects a GStreamer sink. Typical values are ``autoaudiosink``,
-#: ``alsasink``, ``osssink``, ``oss4sink``, ``pulsesink``, and ``shout2send``,
-#: and additional arguments specific to each sink.
-#:
-#: Default::
-#:
-#: OUTPUT = u'autoaudiosink'
-OUTPUT = 'autoaudiosink'
-
-#: Path to the Spotify cache.
-#:
-#: Used by :mod:`mopidy.backends.spotify`.
-#:
-#: Default::
-#:
-#: SPOTIFY_CACHE_PATH = u'$XDG_CACHE_DIR/mopidy/spotify'
-SPOTIFY_CACHE_PATH = '$XDG_CACHE_DIR/mopidy/spotify'
-
-#: Your Spotify Premium username.
-#:
-#: Used by :mod:`mopidy.backends.spotify`.
-SPOTIFY_USERNAME = ''
-
-#: Your Spotify Premium password.
-#:
-#: Used by :mod:`mopidy.backends.spotify`.
-SPOTIFY_PASSWORD = ''
-
-#: Spotify preferred bitrate.
-#:
-#: Available values are 96, 160, and 320.
-#:
-#: Used by :mod:`mopidy.backends.spotify`.
-#:
-#: Default::
-#:
-#: SPOTIFY_BITRATE = 160
-SPOTIFY_BITRATE = 160
-
-#: Spotify proxy host.
-#:
-#: Used by :mod:`mopidy.backends.spotify`.
-#:
-#: Example::
-#:
-#: SPOTIFY_PROXY_HOST = u'protocol://host:port'
-#:
-#: Default::
-#:
-#: SPOTIFY_PROXY_HOST = None
-SPOTIFY_PROXY_HOST = None
-
-#: Spotify proxy username.
-#:
-#: Used by :mod:`mopidy.backends.spotify`.
-#:
-#: Default::
-#:
-#: SPOTIFY_PROXY_USERNAME = None
-SPOTIFY_PROXY_USERNAME = None
-
-#: Spotify proxy password.
-#:
-#: Used by :mod:`mopidy.backends.spotify`.
-#:
-#: Default::
-#:
-#: SPOTIFY_PROXY_PASSWORD = None
-SPOTIFY_PROXY_PASSWORD = None
-
-#: Max number of seconds to wait for Spotify operations to complete.
-#:
-#: Used by :mod:`mopidy.backends.spotify`.
-#:
-#: Default::
-#:
-#: SPOTIFY_TIMEOUT = 10
-SPOTIFY_TIMEOUT = 10
-
-#: Whitelist of URIs to support streaming from.
-#:
-#: Used by :mod:`mopidy.backends.stream`.
-#:
-#: Default::
-#:
-#: STREAM_PROTOCOLS = (
-#: u'http',
-#: u'https',
-#: u'mms',
-#: u'rtmp',
-#: u'rtmps',
-#: u'rtsp',
-#: )
-STREAM_PROTOCOLS = (
- 'http',
- 'https',
- 'mms',
- 'rtmp',
- 'rtmps',
- 'rtsp',
-)
diff --git a/mopidy/utils/path.py b/mopidy/utils/path.py
index 4e5a66cd..88b8be3e 100644
--- a/mopidy/utils/path.py
+++ b/mopidy/utils/path.py
@@ -26,6 +26,8 @@ XDG_DIRS = {
'XDG_MUSIC_DIR': XDG_MUSIC_DIR,
}
DATA_PATH = os.path.join(unicode(XDG_DATA_DIR), 'mopidy')
+CONFIG_PATH = os.path.join(unicode(XDG_CONFIG_DIR), 'mopidy')
+CONFIG_FILE = os.path.join(unicode(CONFIG_PATH), 'mopidy.conf')
SETTINGS_PATH = os.path.join(unicode(XDG_CONFIG_DIR), 'mopidy')
SETTINGS_FILE = os.path.join(unicode(SETTINGS_PATH), 'settings.py')
diff --git a/mopidy/utils/settings.py b/mopidy/utils/settings.py
deleted file mode 100644
index f903a70d..00000000
--- a/mopidy/utils/settings.py
+++ /dev/null
@@ -1,173 +0,0 @@
-# Absolute import needed to import ~/.config/mopidy/settings.py and not
-# ourselves
-from __future__ import absolute_import, unicode_literals
-
-import copy
-import getpass
-import logging
-import os
-import pprint
-import sys
-
-from mopidy import exceptions
-from mopidy.utils import formatting, path
-
-logger = logging.getLogger('mopidy.utils.settings')
-
-
-class SettingsProxy(object):
- def __init__(self, default_settings_module):
- self.default = self._get_settings_dict_from_module(
- default_settings_module)
- self.local = self._get_local_settings()
- self.runtime = {}
-
- def _get_local_settings(self):
- if not os.path.isfile(path.SETTINGS_FILE):
- return {}
- sys.path.insert(0, path.SETTINGS_PATH)
- # pylint: disable = F0401
- import settings as local_settings_module
- # pylint: enable = F0401
- return self._get_settings_dict_from_module(local_settings_module)
-
- def _get_settings_dict_from_module(self, module):
- settings = filter(
- lambda (key, value): self._is_setting(key),
- module.__dict__.iteritems())
- return dict(settings)
-
- def _is_setting(self, name):
- return name.isupper()
-
- @property
- def current(self):
- current = copy.copy(self.default)
- current.update(self.local)
- current.update(self.runtime)
- return current
-
- def __getattr__(self, attr):
- if not self._is_setting(attr):
- return
-
- current = self.current # bind locally to avoid copying+updates
- if attr not in current:
- raise exceptions.SettingsError('Setting "%s" is not set.' % attr)
-
- value = current[attr]
- if isinstance(value, basestring) and len(value) == 0:
- raise exceptions.SettingsError('Setting "%s" is empty.' % attr)
- if not value:
- return value
- if attr.endswith('_PATH') or attr.endswith('_FILE'):
- value = path.expand_path(value)
- return value
-
- def __setattr__(self, attr, value):
- if self._is_setting(attr):
- self.runtime[attr] = value
- else:
- super(SettingsProxy, self).__setattr__(attr, value)
-
- def validate(self):
- if self.get_errors():
- logger.error(
- 'Settings validation errors: %s',
- formatting.indent(self.get_errors_as_string()))
- raise exceptions.SettingsError('Settings validation failed.')
-
- def _read_from_stdin(self, prompt):
- if '_PASSWORD' in prompt:
- return (
- getpass.getpass(prompt)
- .decode(sys.stdin.encoding, 'ignore'))
- else:
- sys.stdout.write(prompt)
- return (
- sys.stdin.readline().strip()
- .decode(sys.stdin.encoding, 'ignore'))
-
- def get_errors(self):
- return validate_settings(self.default, self.local)
-
- def get_errors_as_string(self):
- lines = []
- for (setting, error) in self.get_errors().iteritems():
- lines.append('%s: %s' % (setting, error))
- return '\n'.join(lines)
-
-
-def validate_settings(defaults, settings):
- """
- Checks the settings for both errors like misspellings and against a set of
- rules for renamed settings, etc.
-
- Returns mapping from setting names to associated errors.
-
- :param defaults: Mopidy's default settings
- :type defaults: dict
- :param settings: the user's local settings
- :type settings: dict
- :rtype: dict
- """
- errors = {}
-
- changed = {
- 'DUMP_LOG_FILENAME': 'DEBUG_LOG_FILENAME',
- 'DUMP_LOG_FORMAT': 'DEBUG_LOG_FORMAT',
- 'GSTREAMER_AUDIO_SINK': 'OUTPUT',
- 'LOCAL_MUSIC_FOLDER': 'LOCAL_MUSIC_PATH',
- 'LOCAL_OUTPUT_OVERRIDE': 'OUTPUT',
- 'LOCAL_PLAYLIST_FOLDER': 'LOCAL_PLAYLIST_PATH',
- 'LOCAL_TAG_CACHE': 'LOCAL_TAG_CACHE_FILE',
- 'MIXER_ALSA_CONTROL': None,
- 'MIXER_EXT_PORT': None,
- 'MIXER_EXT_SPEAKERS_A': None,
- 'MIXER_EXT_SPEAKERS_B': None,
- 'MIXER_MAX_VOLUME': None,
- 'SERVER': None,
- 'SERVER_HOSTNAME': 'MPD_SERVER_HOSTNAME',
- 'SERVER_PORT': 'MPD_SERVER_PORT',
- 'SPOTIFY_HIGH_BITRATE': 'SPOTIFY_BITRATE',
- 'SPOTIFY_LIB_APPKEY': None,
- 'SPOTIFY_LIB_CACHE': 'SPOTIFY_CACHE_PATH',
- }
-
- must_be_iterable = [
- 'STREAM_PROTOCOLS',
- ]
-
- for setting, value in settings.iteritems():
- if setting in changed:
- if changed[setting] is None:
- errors[setting] = 'Deprecated setting. It may be removed.'
- else:
- errors[setting] = 'Deprecated setting. Use %s.' % (
- changed[setting],)
-
- elif setting == 'OUTPUTS':
- errors[setting] = (
- 'Deprecated setting, please change to OUTPUT. OUTPUT expects '
- 'a GStreamer bin description string for your desired output.')
-
- elif setting == 'SPOTIFY_BITRATE':
- if value not in (96, 160, 320):
- errors[setting] = (
- 'Unavailable Spotify bitrate. Available bitrates are 96, '
- '160, and 320.')
-
- elif setting.startswith('SHOUTCAST_OUTPUT_'):
- errors[setting] = (
- 'Deprecated setting, please set the value via the GStreamer '
- 'bin in OUTPUT.')
-
- elif setting in must_be_iterable and not hasattr(value, '__iter__'):
- errors[setting] = (
- 'Must be a tuple. '
- "Remember the comma after single values: (u'value',)")
-
- elif setting not in defaults and not setting.startswith('CUSTOM_'):
- errors[setting] = 'Unknown setting.'
-
- return errors
diff --git a/tests/__init__.py b/tests/__init__.py
index 7f7a9c36..b4e1d283 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -8,11 +8,6 @@ if sys.version_info < (2, 7):
else:
import unittest # noqa
-from mopidy import settings
-
-# Nuke any local settings to ensure same test env all over
-settings.local.clear()
-
def path_to_data_dir(name):
path = os.path.dirname(__file__)
diff --git a/tests/audio/actor_test.py b/tests/audio/actor_test.py
index 51786adb..a40b0572 100644
--- a/tests/audio/actor_test.py
+++ b/tests/audio/actor_test.py
@@ -6,7 +6,7 @@ import gst
import pykka
-from mopidy import audio, settings
+from mopidy import audio
from mopidy.utils.path import path_to_uri
from tests import unittest, path_to_data_dir
@@ -14,14 +14,18 @@ from tests import unittest, path_to_data_dir
class AudioTest(unittest.TestCase):
def setUp(self):
- settings.MIXER = 'fakemixer track_max_volume=65536'
- settings.OUTPUT = 'fakesink'
+ config = {
+ 'audio': {
+ 'mixer': 'fakemixer track_max_volume=65536',
+ 'mixer_track': None,
+ 'output': 'fakesink',
+ }
+ }
self.song_uri = path_to_uri(path_to_data_dir('song1.wav'))
- self.audio = audio.Audio.start(config=None).proxy()
+ self.audio = audio.Audio.start(config=config).proxy()
def tearDown(self):
pykka.ActorRegistry.stop_all()
- settings.runtime.clear()
def prepare_uri(self, uri):
self.audio.prepare_change()
@@ -59,8 +63,14 @@ class AudioTest(unittest.TestCase):
self.assertEqual(value, self.audio.get_volume().get())
def test_set_volume_with_mixer_max_below_100(self):
- settings.MIXER = 'fakemixer track_max_volume=40'
- self.audio = audio.Audio.start(config=None).proxy()
+ config = {
+ 'audio': {
+ 'mixer': 'fakemixer track_max_volume=40',
+ 'mixer_track': None,
+ 'output': 'fakesink',
+ }
+ }
+ self.audio = audio.Audio.start(config=config).proxy()
for value in range(0, 101):
self.assertTrue(self.audio.set_volume(value).get())
diff --git a/tests/backends/base/playlists.py b/tests/backends/base/playlists.py
index 00e32a6f..ad5648f9 100644
--- a/tests/backends/base/playlists.py
+++ b/tests/backends/base/playlists.py
@@ -1,25 +1,17 @@
from __future__ import unicode_literals
-import os
-import shutil
-import tempfile
-
import pykka
-from mopidy import audio, core, settings
+from mopidy import audio, core
from mopidy.models import Playlist
-from tests import unittest, path_to_data_dir
+from tests import unittest
class PlaylistsControllerTest(object):
config = {}
def setUp(self):
- settings.LOCAL_PLAYLIST_PATH = tempfile.mkdtemp()
- settings.LOCAL_TAG_CACHE_FILE = path_to_data_dir('library_tag_cache')
- settings.LOCAL_MUSIC_PATH = path_to_data_dir('')
-
self.audio = audio.DummyAudio.start().proxy()
self.backend = self.backend_class.start(
config=self.config, audio=self.audio).proxy()
@@ -28,11 +20,6 @@ class PlaylistsControllerTest(object):
def tearDown(self):
pykka.ActorRegistry.stop_all()
- if os.path.exists(settings.LOCAL_PLAYLIST_PATH):
- shutil.rmtree(settings.LOCAL_PLAYLIST_PATH)
-
- settings.runtime.clear()
-
def test_create_returns_playlist_with_name_set(self):
playlist = self.core.playlists.create('test')
self.assertEqual(playlist.name, 'test')
diff --git a/tests/backends/local/events_test.py b/tests/backends/local/events_test.py
index 5ccf0886..83d77a2f 100644
--- a/tests/backends/local/events_test.py
+++ b/tests/backends/local/events_test.py
@@ -1,4 +1,5 @@
-from mopidy import settings
+from __future__ import unicode_literals
+
from mopidy.backends.local import actor
from tests import unittest, path_to_data_dir
@@ -7,12 +8,10 @@ from tests.backends.base import events
class LocalBackendEventsTest(events.BackendEventsTest, unittest.TestCase):
backend_class = actor.LocalBackend
- # TODO: setup config
-
- def setUp(self):
- settings.LOCAL_TAG_CACHE_FILE = path_to_data_dir('empty_tag_cache')
- super(LocalBackendEventsTest, self).setUp()
-
- def tearDown(self):
- super(LocalBackendEventsTest, self).tearDown()
- settings.runtime.clear()
+ config = {
+ 'local': {
+ 'music_path': path_to_data_dir(''),
+ 'playlist_path': '',
+ 'tag_cache_file': path_to_data_dir('empty_tag_cache'),
+ }
+ }
diff --git a/tests/backends/local/library_test.py b/tests/backends/local/library_test.py
index ca90e40b..e582c788 100644
--- a/tests/backends/local/library_test.py
+++ b/tests/backends/local/library_test.py
@@ -1,6 +1,5 @@
from __future__ import unicode_literals
-from mopidy import settings
from mopidy.backends.local import actor
from tests import unittest, path_to_data_dir
@@ -9,15 +8,10 @@ from tests.backends.base.library import LibraryControllerTest
class LocalLibraryControllerTest(LibraryControllerTest, unittest.TestCase):
backend_class = actor.LocalBackend
- # TODO: setup config
-
- def setUp(self):
- settings.LOCAL_TAG_CACHE_FILE = path_to_data_dir('library_tag_cache')
- settings.LOCAL_MUSIC_PATH = path_to_data_dir('')
-
- super(LocalLibraryControllerTest, self).setUp()
-
- def tearDown(self):
- settings.runtime.clear()
-
- super(LocalLibraryControllerTest, self).tearDown()
+ config = {
+ 'local': {
+ 'music_path': path_to_data_dir(''),
+ 'playlist_path': '',
+ 'tag_cache_file': path_to_data_dir('library_tag_cache'),
+ }
+ }
diff --git a/tests/backends/local/playback_test.py b/tests/backends/local/playback_test.py
index e9b3954c..4c304590 100644
--- a/tests/backends/local/playback_test.py
+++ b/tests/backends/local/playback_test.py
@@ -1,6 +1,5 @@
from __future__ import unicode_literals
-from mopidy import settings
from mopidy.backends.local import actor
from mopidy.core import PlaybackState
from mopidy.models import Track
@@ -13,17 +12,15 @@ from tests.backends.local import generate_song
class LocalPlaybackControllerTest(PlaybackControllerTest, unittest.TestCase):
backend_class = actor.LocalBackend
+ config = {
+ 'local': {
+ 'music_path': path_to_data_dir(''),
+ 'playlist_path': '',
+ 'tag_cache_file': path_to_data_dir('empty_tag_cache'),
+ }
+ }
tracks = [
Track(uri=generate_song(i), length=4464) for i in range(1, 4)]
- # TODO: setup config
-
- def setUp(self):
- settings.LOCAL_TAG_CACHE_FILE = path_to_data_dir('empty_tag_cache')
- super(LocalPlaybackControllerTest, self).setUp()
-
- def tearDown(self):
- super(LocalPlaybackControllerTest, self).tearDown()
- settings.runtime.clear()
def add_track(self, path):
uri = path_to_uri(path_to_data_dir(path))
diff --git a/tests/backends/local/playlists_test.py b/tests/backends/local/playlists_test.py
index 3dbc3a2a..8528adf4 100644
--- a/tests/backends/local/playlists_test.py
+++ b/tests/backends/local/playlists_test.py
@@ -1,8 +1,9 @@
from __future__ import unicode_literals
import os
+import shutil
+import tempfile
-from mopidy import settings
from mopidy.backends.local import actor
from mopidy.models import Track
from mopidy.utils.path import path_to_uri
@@ -17,25 +18,34 @@ class LocalPlaylistsControllerTest(
PlaylistsControllerTest, unittest.TestCase):
backend_class = actor.LocalBackend
- # TODO: setup config
+ config = {
+ 'local': {
+ 'music_path': path_to_data_dir(''),
+ 'tag_cache_file': path_to_data_dir('library_tag_cache'),
+ }
+ }
def setUp(self):
- settings.LOCAL_TAG_CACHE_FILE = path_to_data_dir('empty_tag_cache')
+ self.config['local']['playlist_path'] = tempfile.mkdtemp()
+ self.playlist_path = self.config['local']['playlist_path']
+
super(LocalPlaylistsControllerTest, self).setUp()
def tearDown(self):
super(LocalPlaylistsControllerTest, self).tearDown()
- settings.runtime.clear()
+
+ if os.path.exists(self.playlist_path):
+ shutil.rmtree(self.playlist_path)
def test_created_playlist_is_persisted(self):
- path = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test.m3u')
+ path = os.path.join(self.playlist_path, 'test.m3u')
self.assertFalse(os.path.exists(path))
self.core.playlists.create('test')
self.assertTrue(os.path.exists(path))
def test_create_slugifies_playlist_name(self):
- path = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test-foo-bar.m3u')
+ path = os.path.join(self.playlist_path, 'test-foo-bar.m3u')
self.assertFalse(os.path.exists(path))
playlist = self.core.playlists.create('test FOO baR')
@@ -43,7 +53,7 @@ class LocalPlaylistsControllerTest(
self.assertTrue(os.path.exists(path))
def test_create_slugifies_names_which_tries_to_change_directory(self):
- path = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test-foo-bar.m3u')
+ path = os.path.join(self.playlist_path, 'test-foo-bar.m3u')
self.assertFalse(os.path.exists(path))
playlist = self.core.playlists.create('../../test FOO baR')
@@ -51,8 +61,8 @@ class LocalPlaylistsControllerTest(
self.assertTrue(os.path.exists(path))
def test_saved_playlist_is_persisted(self):
- path1 = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test1.m3u')
- path2 = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test2-foo-bar.m3u')
+ path1 = os.path.join(self.playlist_path, 'test1.m3u')
+ path2 = os.path.join(self.playlist_path, 'test2-foo-bar.m3u')
playlist = self.core.playlists.create('test1')
@@ -67,7 +77,7 @@ class LocalPlaylistsControllerTest(
self.assertTrue(os.path.exists(path2))
def test_deleted_playlist_is_removed(self):
- path = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test.m3u')
+ path = os.path.join(self.playlist_path, 'test.m3u')
self.assertFalse(os.path.exists(path))
playlist = self.core.playlists.create('test')
@@ -90,7 +100,7 @@ class LocalPlaylistsControllerTest(
self.assertEqual(track_path, contents.strip())
def test_playlists_are_loaded_at_startup(self):
- playlist_path = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test.m3u')
+ playlist_path = os.path.join(self.playlist_path, 'test.m3u')
track = Track(uri=path_to_uri(path_to_data_dir('uri2')))
playlist = self.core.playlists.create('test')
diff --git a/tests/backends/local/tracklist_test.py b/tests/backends/local/tracklist_test.py
index 24c400fa..3fc8a0be 100644
--- a/tests/backends/local/tracklist_test.py
+++ b/tests/backends/local/tracklist_test.py
@@ -1,6 +1,5 @@
from __future__ import unicode_literals
-from mopidy import settings
from mopidy.backends.local import actor
from mopidy.models import Track
@@ -11,14 +10,12 @@ from tests.backends.local import generate_song
class LocalTracklistControllerTest(TracklistControllerTest, unittest.TestCase):
backend_class = actor.LocalBackend
+ config = {
+ 'local': {
+ 'music_path': path_to_data_dir(''),
+ 'playlist_path': '',
+ 'tag_cache_file': path_to_data_dir('empty_tag_cache'),
+ }
+ }
tracks = [
Track(uri=generate_song(i), length=4464) for i in range(1, 4)]
- # TODO: setup config
-
- def setUp(self):
- settings.LOCAL_TAG_CACHE_FILE = path_to_data_dir('empty_tag_cache')
- super(LocalTracklistControllerTest, self).setUp()
-
- def tearDown(self):
- super(LocalTracklistControllerTest, self).tearDown()
- settings.runtime.clear()
diff --git a/tests/frontends/http/events_test.py b/tests/frontends/http/events_test.py
index 7661ac6e..c334eefa 100644
--- a/tests/frontends/http/events_test.py
+++ b/tests/frontends/http/events_test.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
import json
try:
@@ -24,7 +26,14 @@ from tests import unittest
@mock.patch('cherrypy.engine.publish')
class HttpEventsTest(unittest.TestCase):
def setUp(self):
- self.http = actor.HttpFrontend(config=None, core=mock.Mock())
+ config = {
+ 'http': {
+ 'hostname': '127.0.0.1',
+ 'port': 6680,
+ 'static_dir': None,
+ }
+ }
+ self.http = actor.HttpFrontend(config=config, core=mock.Mock())
def test_track_playback_paused_is_broadcasted(self, publish):
publish.reset_mock()
diff --git a/tests/frontends/mpd/dispatcher_test.py b/tests/frontends/mpd/dispatcher_test.py
index 3c32cd32..35e18c3b 100644
--- a/tests/frontends/mpd/dispatcher_test.py
+++ b/tests/frontends/mpd/dispatcher_test.py
@@ -13,9 +13,14 @@ from tests import unittest
class MpdDispatcherTest(unittest.TestCase):
def setUp(self):
+ config = {
+ 'mpd': {
+ 'password': None,
+ }
+ }
self.backend = dummy.create_dummy_backend_proxy()
self.core = core.Core.start(backends=[self.backend]).proxy()
- self.dispatcher = MpdDispatcher()
+ self.dispatcher = MpdDispatcher(config=config)
def tearDown(self):
pykka.ActorRegistry.stop_all()
diff --git a/tests/frontends/mpd/protocol/__init__.py b/tests/frontends/mpd/protocol/__init__.py
index 9d24c3fa..21ba1c25 100644
--- a/tests/frontends/mpd/protocol/__init__.py
+++ b/tests/frontends/mpd/protocol/__init__.py
@@ -3,7 +3,7 @@ from __future__ import unicode_literals
import mock
import pykka
-from mopidy import core, settings
+from mopidy import core
from mopidy.backends import dummy
from mopidy.frontends.mpd import session
@@ -23,18 +23,25 @@ class MockConnection(mock.Mock):
class BaseTestCase(unittest.TestCase):
+ def get_config(self):
+ return {
+ 'mpd': {
+ 'password': None,
+ }
+ }
+
def setUp(self):
self.backend = dummy.create_dummy_backend_proxy()
self.core = core.Core.start(backends=[self.backend]).proxy()
self.connection = MockConnection()
- self.session = session.MpdSession(self.connection, core=self.core)
+ self.session = session.MpdSession(
+ self.connection, config=self.get_config(), core=self.core)
self.dispatcher = self.session.dispatcher
self.context = self.dispatcher.context
def tearDown(self):
pykka.ActorRegistry.stop_all()
- settings.runtime.clear()
def sendRequest(self, request):
self.connection.response = []
diff --git a/tests/frontends/mpd/protocol/authentication_test.py b/tests/frontends/mpd/protocol/authentication_test.py
index 26b03f45..2597ddef 100644
--- a/tests/frontends/mpd/protocol/authentication_test.py
+++ b/tests/frontends/mpd/protocol/authentication_test.py
@@ -1,63 +1,56 @@
from __future__ import unicode_literals
-from mopidy import settings
-
from tests.frontends.mpd import protocol
-class AuthenticationTest(protocol.BaseTestCase):
- def test_authentication_with_valid_password_is_accepted(self):
- settings.MPD_SERVER_PASSWORD = u'topsecret'
+class AuthenticationActiveTest(protocol.BaseTestCase):
+ def get_config(self):
+ config = super(AuthenticationActiveTest, self).get_config()
+ config['mpd']['password'] = 'topsecret'
+ return config
+ def test_authentication_with_valid_password_is_accepted(self):
self.sendRequest('password "topsecret"')
self.assertTrue(self.dispatcher.authenticated)
self.assertInResponse('OK')
def test_authentication_with_invalid_password_is_not_accepted(self):
- settings.MPD_SERVER_PASSWORD = u'topsecret'
-
self.sendRequest('password "secret"')
self.assertFalse(self.dispatcher.authenticated)
self.assertEqualResponse('ACK [3@0] {password} incorrect password')
- def test_authentication_with_anything_when_password_check_turned_off(self):
- settings.MPD_SERVER_PASSWORD = None
-
- self.sendRequest('any request at all')
- self.assertTrue(self.dispatcher.authenticated)
- self.assertEqualResponse('ACK [5@0] {} unknown command "any"')
-
def test_anything_when_not_authenticated_should_fail(self):
- settings.MPD_SERVER_PASSWORD = u'topsecret'
-
self.sendRequest('any request at all')
self.assertFalse(self.dispatcher.authenticated)
self.assertEqualResponse(
u'ACK [4@0] {any} you don\'t have permission for "any"')
def test_close_is_allowed_without_authentication(self):
- settings.MPD_SERVER_PASSWORD = u'topsecret'
-
self.sendRequest('close')
self.assertFalse(self.dispatcher.authenticated)
def test_commands_is_allowed_without_authentication(self):
- settings.MPD_SERVER_PASSWORD = u'topsecret'
-
self.sendRequest('commands')
self.assertFalse(self.dispatcher.authenticated)
self.assertInResponse('OK')
def test_notcommands_is_allowed_without_authentication(self):
- settings.MPD_SERVER_PASSWORD = u'topsecret'
-
self.sendRequest('notcommands')
self.assertFalse(self.dispatcher.authenticated)
self.assertInResponse('OK')
def test_ping_is_allowed_without_authentication(self):
- settings.MPD_SERVER_PASSWORD = u'topsecret'
-
self.sendRequest('ping')
self.assertFalse(self.dispatcher.authenticated)
self.assertInResponse('OK')
+
+
+class AuthenticationInactiveTest(protocol.BaseTestCase):
+ def test_authentication_with_anything_when_password_check_turned_off(self):
+ self.sendRequest('any request at all')
+ self.assertTrue(self.dispatcher.authenticated)
+ self.assertEqualResponse('ACK [5@0] {} unknown command "any"')
+
+ def test_any_password_is_not_accepted_when_password_check_turned_off(self):
+ self.sendRequest('password "secret"')
+ self.assertEqualResponse('ACK [3@0] {password} incorrect password')
diff --git a/tests/frontends/mpd/protocol/connection_test.py b/tests/frontends/mpd/protocol/connection_test.py
index 840ce48f..01deb7a7 100644
--- a/tests/frontends/mpd/protocol/connection_test.py
+++ b/tests/frontends/mpd/protocol/connection_test.py
@@ -2,8 +2,6 @@ from __future__ import unicode_literals
from mock import patch
-from mopidy import settings
-
from tests.frontends.mpd import protocol
@@ -26,21 +24,6 @@ class ConnectionHandlerTest(protocol.BaseTestCase):
self.assertEqualResponse(
'ACK [4@0] {kill} you don\'t have permission for "kill"')
- def test_valid_password_is_accepted(self):
- settings.MPD_SERVER_PASSWORD = 'topsecret'
- self.sendRequest('password "topsecret"')
- self.assertEqualResponse('OK')
-
- def test_invalid_password_is_not_accepted(self):
- settings.MPD_SERVER_PASSWORD = 'topsecret'
- self.sendRequest('password "secret"')
- self.assertEqualResponse('ACK [3@0] {password} incorrect password')
-
- def test_any_password_is_not_accepted_when_password_check_turned_off(self):
- settings.MPD_SERVER_PASSWORD = None
- self.sendRequest('password "secret"')
- self.assertEqualResponse('ACK [3@0] {password} incorrect password')
-
def test_ping(self):
self.sendRequest('ping')
self.assertEqualResponse('OK')
diff --git a/tests/frontends/mpd/protocol/reflection_test.py b/tests/frontends/mpd/protocol/reflection_test.py
index f2720473..16f4579f 100644
--- a/tests/frontends/mpd/protocol/reflection_test.py
+++ b/tests/frontends/mpd/protocol/reflection_test.py
@@ -1,7 +1,5 @@
from __future__ import unicode_literals
-from mopidy import settings
-
from tests.frontends.mpd import protocol
@@ -29,19 +27,6 @@ class ReflectionHandlerTest(protocol.BaseTestCase):
self.assertNotInResponse('command: sticker')
self.assertInResponse('OK')
- def test_commands_show_less_if_auth_required_and_not_authed(self):
- settings.MPD_SERVER_PASSWORD = u'secret'
- self.sendRequest('commands')
- # Not requiring auth
- self.assertInResponse('command: close')
- self.assertInResponse('command: commands')
- self.assertInResponse('command: notcommands')
- self.assertInResponse('command: password')
- self.assertInResponse('command: ping')
- # Requiring auth
- self.assertNotInResponse('command: play')
- self.assertNotInResponse('command: status')
-
def test_decoders(self):
self.sendRequest('decoders')
self.assertInResponse('OK')
@@ -53,8 +38,35 @@ class ReflectionHandlerTest(protocol.BaseTestCase):
self.assertInResponse('command: kill')
self.assertInResponse('OK')
+ def test_tagtypes(self):
+ self.sendRequest('tagtypes')
+ self.assertInResponse('OK')
+
+ def test_urlhandlers(self):
+ self.sendRequest('urlhandlers')
+ self.assertInResponse('OK')
+ self.assertInResponse('handler: dummy')
+
+
+class ReflectionWhenNotAuthedTest(protocol.BaseTestCase):
+ def get_config(self):
+ config = super(ReflectionWhenNotAuthedTest, self).get_config()
+ config['mpd']['password'] = 'topsecret'
+ return config
+
+ def test_commands_show_less_if_auth_required_and_not_authed(self):
+ self.sendRequest('commands')
+ # Not requiring auth
+ self.assertInResponse('command: close')
+ self.assertInResponse('command: commands')
+ self.assertInResponse('command: notcommands')
+ self.assertInResponse('command: password')
+ self.assertInResponse('command: ping')
+ # Requiring auth
+ self.assertNotInResponse('command: play')
+ self.assertNotInResponse('command: status')
+
def test_notcommands_returns_more_if_auth_required_and_not_authed(self):
- settings.MPD_SERVER_PASSWORD = u'secret'
self.sendRequest('notcommands')
# Not requiring auth
self.assertNotInResponse('command: close')
@@ -65,12 +77,3 @@ class ReflectionHandlerTest(protocol.BaseTestCase):
# Requiring auth
self.assertInResponse('command: play')
self.assertInResponse('command: status')
-
- def test_tagtypes(self):
- self.sendRequest('tagtypes')
- self.assertInResponse('OK')
-
- def test_urlhandlers(self):
- self.sendRequest('urlhandlers')
- self.assertInResponse('OK')
- self.assertInResponse('handler: dummy')
diff --git a/tests/frontends/mpd/translator_test.py b/tests/frontends/mpd/translator_test.py
index 088ae137..828acf1a 100644
--- a/tests/frontends/mpd/translator_test.py
+++ b/tests/frontends/mpd/translator_test.py
@@ -3,7 +3,6 @@ from __future__ import unicode_literals
import datetime
import os
-from mopidy import settings
from mopidy.utils.path import mtime, uri_to_path
from mopidy.frontends.mpd import translator, protocol
from mopidy.models import Album, Artist, TlTrack, Playlist, Track
@@ -24,11 +23,10 @@ class TrackMpdFormatTest(unittest.TestCase):
)
def setUp(self):
- settings.LOCAL_MUSIC_PATH = '/dir/subdir'
+ self.music_path = '/dir/subdir'
mtime.set_fake_time(1234567)
def tearDown(self):
- settings.runtime.clear()
mtime.undo_fake()
def test_track_to_mpd_format_for_empty_track(self):
@@ -137,15 +135,14 @@ class QueryFromMpdListFormatTest(unittest.TestCase):
class TracksToTagCacheFormatTest(unittest.TestCase):
def setUp(self):
- settings.LOCAL_MUSIC_PATH = '/dir/subdir'
+ self.music_path = '/dir/subdir'
mtime.set_fake_time(1234567)
def tearDown(self):
- settings.runtime.clear()
mtime.undo_fake()
def translate(self, track):
- base_path = settings.LOCAL_MUSIC_PATH.encode('utf-8')
+ base_path = self.music_path.encode('utf-8')
result = dict(translator.track_to_mpd_format(track))
result['file'] = uri_to_path(result['file'])[len(base_path) + 1:]
result['key'] = os.path.basename(result['file'])
@@ -177,11 +174,11 @@ class TracksToTagCacheFormatTest(unittest.TestCase):
self.fail("Couldn't find end %s in result" % directory)
def test_empty_tag_cache_has_header(self):
- result = translator.tracks_to_tag_cache_format([])
+ result = translator.tracks_to_tag_cache_format([], self.music_path)
result = self.consume_headers(result)
def test_empty_tag_cache_has_song_list(self):
- result = translator.tracks_to_tag_cache_format([])
+ result = translator.tracks_to_tag_cache_format([], self.music_path)
result = self.consume_headers(result)
song_list, result = self.consume_song_list(result)
@@ -190,12 +187,12 @@ class TracksToTagCacheFormatTest(unittest.TestCase):
def test_tag_cache_has_header(self):
track = Track(uri='file:///dir/subdir/song.mp3')
- result = translator.tracks_to_tag_cache_format([track])
+ result = translator.tracks_to_tag_cache_format([track], self.music_path)
result = self.consume_headers(result)
def test_tag_cache_has_song_list(self):
track = Track(uri='file:///dir/subdir/song.mp3')
- result = translator.tracks_to_tag_cache_format([track])
+ result = translator.tracks_to_tag_cache_format([track], self.music_path)
result = self.consume_headers(result)
song_list, result = self.consume_song_list(result)
@@ -205,7 +202,7 @@ class TracksToTagCacheFormatTest(unittest.TestCase):
def test_tag_cache_has_formated_track(self):
track = Track(uri='file:///dir/subdir/song.mp3')
formated = self.translate(track)
- result = translator.tracks_to_tag_cache_format([track])
+ result = translator.tracks_to_tag_cache_format([track], self.music_path)
result = self.consume_headers(result)
song_list, result = self.consume_song_list(result)
@@ -216,7 +213,7 @@ class TracksToTagCacheFormatTest(unittest.TestCase):
def test_tag_cache_has_formated_track_with_key_and_mtime(self):
track = Track(uri='file:///dir/subdir/song.mp3')
formated = self.translate(track)
- result = translator.tracks_to_tag_cache_format([track])
+ result = translator.tracks_to_tag_cache_format([track], self.music_path)
result = self.consume_headers(result)
song_list, result = self.consume_song_list(result)
@@ -227,7 +224,7 @@ class TracksToTagCacheFormatTest(unittest.TestCase):
def test_tag_cache_suports_directories(self):
track = Track(uri='file:///dir/subdir/folder/song.mp3')
formated = self.translate(track)
- result = translator.tracks_to_tag_cache_format([track])
+ result = translator.tracks_to_tag_cache_format([track], self.music_path)
result = self.consume_headers(result)
folder, result = self.consume_directory(result)
@@ -241,7 +238,7 @@ class TracksToTagCacheFormatTest(unittest.TestCase):
def test_tag_cache_diretory_header_is_right(self):
track = Track(uri='file:///dir/subdir/folder/sub/song.mp3')
- result = translator.tracks_to_tag_cache_format([track])
+ result = translator.tracks_to_tag_cache_format([track], self.music_path)
result = self.consume_headers(result)
folder, result = self.consume_directory(result)
@@ -253,7 +250,7 @@ class TracksToTagCacheFormatTest(unittest.TestCase):
def test_tag_cache_suports_sub_directories(self):
track = Track(uri='file:///dir/subdir/folder/sub/song.mp3')
formated = self.translate(track)
- result = translator.tracks_to_tag_cache_format([track])
+ result = translator.tracks_to_tag_cache_format([track], self.music_path)
result = self.consume_headers(result)
@@ -281,7 +278,7 @@ class TracksToTagCacheFormatTest(unittest.TestCase):
formated.extend(self.translate(tracks[0]))
formated.extend(self.translate(tracks[1]))
- result = translator.tracks_to_tag_cache_format(tracks)
+ result = translator.tracks_to_tag_cache_format(tracks, self.music_path)
result = self.consume_headers(result)
song_list, result = self.consume_song_list(result)
@@ -299,7 +296,7 @@ class TracksToTagCacheFormatTest(unittest.TestCase):
formated.append(self.translate(tracks[0]))
formated.append(self.translate(tracks[1]))
- result = translator.tracks_to_tag_cache_format(tracks)
+ result = translator.tracks_to_tag_cache_format(tracks, self.music_path)
result = self.consume_headers(result)
folder, result = self.consume_directory(result)
@@ -315,13 +312,10 @@ class TracksToTagCacheFormatTest(unittest.TestCase):
class TracksToDirectoryTreeTest(unittest.TestCase):
def setUp(self):
- settings.LOCAL_MUSIC_PATH = '/root/'
-
- def tearDown(self):
- settings.runtime.clear()
+ self.music_path = '/root'
def test_no_tracks_gives_emtpy_tree(self):
- tree = translator.tracks_to_directory_tree([])
+ tree = translator.tracks_to_directory_tree([], self.music_path)
self.assertEqual(tree, ({}, []))
def test_top_level_files(self):
@@ -330,18 +324,18 @@ class TracksToDirectoryTreeTest(unittest.TestCase):
Track(uri='file:///root/file2.mp3'),
Track(uri='file:///root/file3.mp3'),
]
- tree = translator.tracks_to_directory_tree(tracks)
+ tree = translator.tracks_to_directory_tree(tracks, self.music_path)
self.assertEqual(tree, ({}, tracks))
def test_single_file_in_subdir(self):
tracks = [Track(uri='file:///root/dir/file1.mp3')]
- tree = translator.tracks_to_directory_tree(tracks)
+ tree = translator.tracks_to_directory_tree(tracks, self.music_path)
expected = ({'dir': ({}, tracks)}, [])
self.assertEqual(tree, expected)
def test_single_file_in_sub_subdir(self):
tracks = [Track(uri='file:///root/dir1/dir2/file1.mp3')]
- tree = translator.tracks_to_directory_tree(tracks)
+ tree = translator.tracks_to_directory_tree(tracks, self.music_path)
expected = ({'dir1': ({'dir1/dir2': ({}, tracks)}, [])}, [])
self.assertEqual(tree, expected)
@@ -353,7 +347,7 @@ class TracksToDirectoryTreeTest(unittest.TestCase):
Track(uri='file:///root/dir2/file4.mp3'),
Track(uri='file:///root/dir2/sub/file5.mp3'),
]
- tree = translator.tracks_to_directory_tree(tracks)
+ tree = translator.tracks_to_directory_tree(tracks, self.music_path)
expected = (
{
'dir1': ({}, [tracks[1], tracks[2]]),
diff --git a/tests/frontends/mpris/player_interface_test.py b/tests/frontends/mpris/player_interface_test.py
index ec4a17a9..e1e13084 100644
--- a/tests/frontends/mpris/player_interface_test.py
+++ b/tests/frontends/mpris/player_interface_test.py
@@ -28,7 +28,7 @@ class PlayerInterfaceTest(unittest.TestCase):
objects.MprisObject._connect_to_dbus = mock.Mock()
self.backend = dummy.create_dummy_backend_proxy()
self.core = core.Core.start(backends=[self.backend]).proxy()
- self.mpris = objects.MprisObject(core=self.core)
+ self.mpris = objects.MprisObject(config={}, core=self.core)
def tearDown(self):
pykka.ActorRegistry.stop_all()
diff --git a/tests/frontends/mpris/playlists_interface_test.py b/tests/frontends/mpris/playlists_interface_test.py
index 745a858c..67f9e9be 100644
--- a/tests/frontends/mpris/playlists_interface_test.py
+++ b/tests/frontends/mpris/playlists_interface_test.py
@@ -25,7 +25,7 @@ class PlayerInterfaceTest(unittest.TestCase):
objects.MprisObject._connect_to_dbus = mock.Mock()
self.backend = dummy.create_dummy_backend_proxy()
self.core = core.Core.start(backends=[self.backend]).proxy()
- self.mpris = objects.MprisObject(core=self.core)
+ self.mpris = objects.MprisObject(config={}, core=self.core)
foo = self.core.playlists.create('foo').get()
foo = foo.copy(last_modified=datetime.datetime(2012, 3, 1, 6, 0, 0))
diff --git a/tests/frontends/mpris/root_interface_test.py b/tests/frontends/mpris/root_interface_test.py
index 36d689a2..806b2162 100644
--- a/tests/frontends/mpris/root_interface_test.py
+++ b/tests/frontends/mpris/root_interface_test.py
@@ -5,7 +5,7 @@ import sys
import mock
import pykka
-from mopidy import core, exceptions, settings
+from mopidy import core, exceptions
from mopidy.backends import dummy
try:
@@ -19,11 +19,17 @@ from tests import unittest
@unittest.skipUnless(sys.platform.startswith('linux'), 'requires Linux')
class RootInterfaceTest(unittest.TestCase):
def setUp(self):
+ config = {
+ 'mpris': {
+ 'desktop_file': '/tmp/foo.desktop',
+ }
+ }
+
objects.exit_process = mock.Mock()
objects.MprisObject._connect_to_dbus = mock.Mock()
self.backend = dummy.create_dummy_backend_proxy()
self.core = core.Core.start(backends=[self.backend]).proxy()
- self.mpris = objects.MprisObject(core=self.core)
+ self.mpris = objects.MprisObject(config=config, core=self.core)
def tearDown(self):
pykka.ActorRegistry.stop_all()
@@ -66,15 +72,9 @@ class RootInterfaceTest(unittest.TestCase):
result = self.mpris.Get(objects.ROOT_IFACE, 'Identity')
self.assertEquals(result, 'Mopidy')
- def test_desktop_entry_is_mopidy(self):
- result = self.mpris.Get(objects.ROOT_IFACE, 'DesktopEntry')
- self.assertEquals(result, 'mopidy')
-
def test_desktop_entry_is_based_on_DESKTOP_FILE_setting(self):
- settings.runtime['DESKTOP_FILE'] = '/tmp/foo.desktop'
result = self.mpris.Get(objects.ROOT_IFACE, 'DesktopEntry')
self.assertEquals(result, 'foo')
- settings.runtime.clear()
def test_supported_uri_schemes_includes_backend_uri_schemes(self):
result = self.mpris.Get(objects.ROOT_IFACE, 'SupportedUriSchemes')
diff --git a/tests/utils/settings_test.py b/tests/utils/settings_test.py
deleted file mode 100644
index ce763486..00000000
--- a/tests/utils/settings_test.py
+++ /dev/null
@@ -1,150 +0,0 @@
-from __future__ import unicode_literals
-
-import os
-
-from mopidy import exceptions, settings
-from mopidy.utils import settings as setting_utils
-
-from tests import unittest
-
-
-class ValidateSettingsTest(unittest.TestCase):
- def setUp(self):
- self.defaults = {
- 'MPD_SERVER_HOSTNAME': '::',
- 'MPD_SERVER_PORT': 6600,
- 'SPOTIFY_BITRATE': 160,
- }
-
- def test_no_errors_yields_empty_dict(self):
- result = setting_utils.validate_settings(self.defaults, {})
- self.assertEqual(result, {})
-
- def test_unknown_setting_returns_error(self):
- result = setting_utils.validate_settings(
- self.defaults, {'MPD_SERVER_HOSTNMAE': '127.0.0.1'})
- self.assertEqual(
- result['MPD_SERVER_HOSTNMAE'], 'Unknown setting.')
-
- def test_custom_settings_does_not_return_errors(self):
- result = setting_utils.validate_settings(
- self.defaults, {'CUSTOM_MYAPP_SETTING': 'foobar'})
- self.assertNotIn('CUSTOM_MYAPP_SETTING', result)
-
- def test_not_renamed_setting_returns_error(self):
- result = setting_utils.validate_settings(
- self.defaults, {'SERVER_HOSTNAME': '127.0.0.1'})
- self.assertEqual(
- result['SERVER_HOSTNAME'],
- 'Deprecated setting. Use MPD_SERVER_HOSTNAME.')
-
- def test_unneeded_settings_returns_error(self):
- result = setting_utils.validate_settings(
- self.defaults, {'SPOTIFY_LIB_APPKEY': '/tmp/foo'})
- self.assertEqual(
- result['SPOTIFY_LIB_APPKEY'],
- 'Deprecated setting. It may be removed.')
-
- def test_unavailable_bitrate_setting_returns_error(self):
- result = setting_utils.validate_settings(
- self.defaults, {'SPOTIFY_BITRATE': 50})
- self.assertEqual(
- result['SPOTIFY_BITRATE'],
- 'Unavailable Spotify bitrate. '
- 'Available bitrates are 96, 160, and 320.')
-
- def test_two_errors_are_both_reported(self):
- result = setting_utils.validate_settings(
- self.defaults, {'FOO': '', 'BAR': ''})
- self.assertEqual(len(result), 2)
-
-
-class SettingsProxyTest(unittest.TestCase):
- def setUp(self):
- self.settings = setting_utils.SettingsProxy(settings)
- self.settings.local.clear()
-
- def test_set_and_get_attr(self):
- self.settings.TEST = 'test'
- self.assertEqual(self.settings.TEST, 'test')
-
- def test_getattr_raises_error_on_missing_setting(self):
- try:
- self.settings.TEST
- self.fail('Should raise exception')
- except exceptions.SettingsError as e:
- self.assertEqual('Setting "TEST" is not set.', e.message)
-
- def test_getattr_raises_error_on_empty_setting(self):
- self.settings.TEST = ''
- try:
- self.settings.TEST
- self.fail('Should raise exception')
- except exceptions.SettingsError as e:
- self.assertEqual('Setting "TEST" is empty.', e.message)
-
- def test_getattr_does_not_raise_error_if_setting_is_false(self):
- self.settings.TEST = False
- self.assertEqual(False, self.settings.TEST)
-
- def test_getattr_does_not_raise_error_if_setting_is_none(self):
- self.settings.TEST = None
- self.assertEqual(None, self.settings.TEST)
-
- def test_getattr_does_not_raise_error_if_setting_is_zero(self):
- self.settings.TEST = 0
- self.assertEqual(0, self.settings.TEST)
-
- def test_setattr_updates_runtime_settings(self):
- self.settings.TEST = 'test'
- self.assertIn('TEST', self.settings.runtime)
-
- def test_setattr_updates_runtime_with_value(self):
- self.settings.TEST = 'test'
- self.assertEqual(self.settings.runtime['TEST'], 'test')
-
- def test_runtime_value_included_in_current(self):
- self.settings.TEST = 'test'
- self.assertEqual(self.settings.current['TEST'], 'test')
-
- def test_value_ending_in_path_is_expanded(self):
- self.settings.TEST_PATH = '~/test'
- actual = self.settings.TEST_PATH
- expected = os.path.expanduser('~/test')
- self.assertEqual(actual, expected)
-
- def test_value_ending_in_path_is_absolute(self):
- self.settings.TEST_PATH = './test'
- actual = self.settings.TEST_PATH
- expected = os.path.abspath('./test')
- self.assertEqual(actual, expected)
-
- def test_value_ending_in_file_is_expanded(self):
- self.settings.TEST_FILE = '~/test'
- actual = self.settings.TEST_FILE
- expected = os.path.expanduser('~/test')
- self.assertEqual(actual, expected)
-
- def test_value_ending_in_file_is_absolute(self):
- self.settings.TEST_FILE = './test'
- actual = self.settings.TEST_FILE
- expected = os.path.abspath('./test')
- self.assertEqual(actual, expected)
-
- def test_value_not_ending_in_path_or_file_is_not_expanded(self):
- self.settings.TEST = '~/test'
- actual = self.settings.TEST
- self.assertEqual(actual, '~/test')
-
- def test_value_not_ending_in_path_or_file_is_not_absolute(self):
- self.settings.TEST = './test'
- actual = self.settings.TEST
- self.assertEqual(actual, './test')
-
- def test_value_ending_in_file_can_be_none(self):
- self.settings.TEST_FILE = None
- self.assertEqual(self.settings.TEST_FILE, None)
-
- def test_value_ending_in_path_can_be_none(self):
- self.settings.TEST_PATH = None
- self.assertEqual(self.settings.TEST_PATH, None)