Make all strings unicode by default (fixes #224)

This commit is contained in:
Stein Magnus Jodal 2012-11-13 00:17:45 +01:00
parent a2d7f2f504
commit 6acaa490e9
141 changed files with 1595 additions and 1297 deletions

View File

@ -1,5 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
from __future__ import unicode_literals
import sys import sys
import logging import logging
@ -8,20 +10,26 @@ from mopidy.utils.log import setup_console_logging, setup_root_logger
from mopidy.scanner import Scanner, translator from mopidy.scanner import Scanner, translator
from mopidy.frontends.mpd.translator import tracks_to_tag_cache_format from mopidy.frontends.mpd.translator import tracks_to_tag_cache_format
setup_root_logger() setup_root_logger()
setup_console_logging(2) setup_console_logging(2)
tracks = [] tracks = []
def store(data): def store(data):
track = translator(data) track = translator(data)
tracks.append(track) tracks.append(track)
logging.debug(u'Added %s', track.uri) logging.debug('Added %s', track.uri)
def debug(uri, error, debug): def debug(uri, error, debug):
logging.error(u'Failed %s: %s - %s', uri, error, debug) logging.error('Failed %s: %s - %s', uri, error, debug)
logging.info('Scanning %s', settings.LOCAL_MUSIC_PATH)
logging.info(u'Scanning %s', settings.LOCAL_MUSIC_PATH)
scanner = Scanner(settings.LOCAL_MUSIC_PATH, store, debug) scanner = Scanner(settings.LOCAL_MUSIC_PATH, store, debug)
try: try:
@ -29,10 +37,12 @@ try:
except KeyboardInterrupt: except KeyboardInterrupt:
scanner.stop() scanner.stop()
logging.info(u'Done')
logging.info('Done')
for a in tracks_to_tag_cache_format(tracks): for a in tracks_to_tag_cache_format(tracks):
if len(a) == 1: if len(a) == 1:
print (u'%s' % a).encode('utf-8') print ('%s' % a).encode('utf-8')
else: else:
print (u'%s: %s' % a).encode('utf-8') print ('%s: %s' % a).encode('utf-8')

View File

@ -93,6 +93,9 @@ backends:
:attr:`mopidy.settings.DEBUG_THREAD` setting a ``SIGUSR1`` signal will dump :attr:`mopidy.settings.DEBUG_THREAD` setting a ``SIGUSR1`` signal will dump
the traceback for all running threads. the traceback for all running threads.
- Make the entire code base use unicode strings by default, and only fall back
to bytestrings where it is required. Another step closer to Python 3.
**Bug fixes** **Bug fixes**
- :issue:`218`: The MPD commands ``listplaylist`` and ``listplaylistinfo`` now - :issue:`218`: The MPD commands ``listplaylist`` and ``listplaylistinfo`` now

View File

@ -12,6 +12,8 @@
# All configuration values have a default; values that are commented out # All configuration values have a default; values that are commented out
# serve to show the default. # serve to show the default.
from __future__ import unicode_literals
import os import os
import sys import sys
@ -89,8 +91,8 @@ source_suffix = '.rst'
master_doc = 'index' master_doc = 'index'
# General information about the project. # General information about the project.
project = u'Mopidy' project = 'Mopidy'
copyright = u'2010-2012, Stein Magnus Jodal and contributors' copyright = '2010-2012, Stein Magnus Jodal and contributors'
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the
@ -231,8 +233,8 @@ latex_documents = [
( (
'index', 'index',
'Mopidy.tex', 'Mopidy.tex',
u'Mopidy Documentation', 'Mopidy Documentation',
u'Stein Magnus Jodal', 'Stein Magnus Jodal',
'manual' 'manual'
), ),
] ]

View File

@ -84,6 +84,22 @@ contributing.
Code style Code style
========== ==========
- Always import ``unicode_literals`` and use unicode literals for everything
except where you're explicitly working with bytes, which are marked with the
``b`` prefix.
Do this::
from __future__ import unicode_literals
foo = 'I am a unicode string, which is a sane default'
bar = b'I am a bytestring'
Not this::
foo = u'I am a unicode string'
bar = 'I am a bytestring, but was it intentional?'
- Follow :pep:`8` unless otherwise noted. `pep8.py - Follow :pep:`8` unless otherwise noted. `pep8.py
<http://pypi.python.org/pypi/pep8/>`_ or `flake8 <http://pypi.python.org/pypi/pep8/>`_ or `flake8
<http://pypi.python.org/pypi/flake8>`_ can be used to check your code <http://pypi.python.org/pypi/flake8>`_ can be used to check your code

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
# pylint: disable = E0611,F0401 # pylint: disable = E0611,F0401
from distutils.version import StrictVersion as SV from distutils.version import StrictVersion as SV
# pylint: enable = E0611,F0401 # pylint: enable = E0611,F0401
@ -9,13 +11,13 @@ import pykka
if not (2, 6) <= sys.version_info < (3,): if not (2, 6) <= sys.version_info < (3,):
sys.exit( sys.exit(
u'Mopidy requires Python >= 2.6, < 3, but found %s' % 'Mopidy requires Python >= 2.6, < 3, but found %s' %
'.'.join(map(str, sys.version_info[:3]))) '.'.join(map(str, sys.version_info[:3])))
if (isinstance(pykka.__version__, basestring) if (isinstance(pykka.__version__, basestring)
and not SV('1.0') <= SV(pykka.__version__) < SV('2.0')): and not SV('1.0') <= SV(pykka.__version__) < SV('2.0')):
sys.exit( sys.exit(
u'Mopidy requires Pykka >= 1.0, < 2, but found %s' % pykka.__version__) 'Mopidy requires Pykka >= 1.0, < 2, but found %s' % pykka.__version__)
warnings.filterwarnings('ignore', 'could not open display') warnings.filterwarnings('ignore', 'could not open display')

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
import optparse import optparse
import os import os
@ -62,7 +64,7 @@ def main():
except exceptions.SettingsError as ex: except exceptions.SettingsError as ex:
logger.error(ex.message) logger.error(ex.message)
except KeyboardInterrupt: except KeyboardInterrupt:
logger.info(u'Interrupted. Exiting...') logger.info('Interrupted. Exiting...')
except Exception as ex: except Exception as ex:
logger.exception(ex) logger.exception(ex)
finally: finally:
@ -76,7 +78,7 @@ def main():
def parse_options(): def parse_options():
parser = optparse.OptionParser( parser = optparse.OptionParser(
version=u'Mopidy %s' % versioning.get_version()) version='Mopidy %s' % versioning.get_version())
parser.add_option( parser.add_option(
'--help-gst', '--help-gst',
action='store_true', dest='help_gst', action='store_true', dest='help_gst',
@ -114,15 +116,15 @@ def parse_options():
def check_old_folders(): def check_old_folders():
old_settings_folder = os.path.expanduser(u'~/.mopidy') old_settings_folder = os.path.expanduser('~/.mopidy')
if not os.path.isdir(old_settings_folder): if not os.path.isdir(old_settings_folder):
return return
logger.warning( logger.warning(
u'Old settings folder found at %s, settings.py should be moved ' 'Old settings folder found at %s, settings.py should be moved '
u'to %s, any cache data should be deleted. See release notes for ' 'to %s, any cache data should be deleted. See release notes for '
u'further instructions.', old_settings_folder, path.SETTINGS_PATH) 'further instructions.', old_settings_folder, path.SETTINGS_PATH)
def setup_settings(interactive): def setup_settings(interactive):
@ -171,7 +173,7 @@ def setup_frontends(core):
try: try:
importing.get_class(frontend_class_name).start(core=core) importing.get_class(frontend_class_name).start(core=core)
except exceptions.OptionalDependencyError as ex: except exceptions.OptionalDependencyError as ex:
logger.info(u'Disabled: %s (%s)', frontend_class_name, ex) logger.info('Disabled: %s (%s)', frontend_class_name, ex)
def stop_frontends(): def stop_frontends():

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
# flake8: noqa # flake8: noqa
from .actor import Audio from .actor import Audio
from .listener import AudioListener from .listener import AudioListener

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import pygst import pygst
pygst.require('0.10') pygst.require('0.10')
import gst import gst
@ -70,9 +72,9 @@ class Audio(pykka.ThreadingActor):
# These caps matches the audio data provided by libspotify # These caps matches the audio data provided by libspotify
default_caps = gst.Caps( default_caps = gst.Caps(
'audio/x-raw-int, endianness=(int)1234, channels=(int)2, ' b'audio/x-raw-int, endianness=(int)1234, channels=(int)2, '
'width=(int)16, depth=(int)16, signed=(boolean)true, ' b'width=(int)16, depth=(int)16, signed=(boolean)true, '
'rate=(int)44100') b'rate=(int)44100')
source = element.get_property('source') source = element.get_property('source')
source.set_property('caps', default_caps) source.set_property('caps', default_caps)
@ -109,7 +111,7 @@ class Audio(pykka.ThreadingActor):
return return
# We assume that the bin will contain a single mixer. # We assume that the bin will contain a single mixer.
mixer = mixerbin.get_by_interface('GstMixer') mixer = mixerbin.get_by_interface(b'GstMixer')
if not mixer: if not mixer:
logger.warning( logger.warning(
'Did not find any audio mixers in "%s"', settings.MIXER) 'Did not find any audio mixers in "%s"', settings.MIXER)
@ -162,14 +164,14 @@ class Audio(pykka.ThreadingActor):
self._trigger_reached_end_of_stream_event() self._trigger_reached_end_of_stream_event()
elif message.type == gst.MESSAGE_ERROR: elif message.type == gst.MESSAGE_ERROR:
error, debug = message.parse_error() error, debug = message.parse_error()
logger.error(u'%s %s', error, debug) logger.error('%s %s', error, debug)
self.stop_playback() self.stop_playback()
elif message.type == gst.MESSAGE_WARNING: elif message.type == gst.MESSAGE_WARNING:
error, debug = message.parse_warning() error, debug = message.parse_warning()
logger.warning(u'%s %s', error, debug) logger.warning('%s %s', error, debug)
def _trigger_reached_end_of_stream_event(self): def _trigger_reached_end_of_stream_event(self):
logger.debug(u'Triggering reached end of stream event') logger.debug('Triggering reached end of stream event')
AudioListener.send('reached_end_of_stream') AudioListener.send('reached_end_of_stream')
def set_uri(self, uri): def set_uri(self, uri):
@ -389,12 +391,12 @@ class Audio(pykka.ThreadingActor):
# Default to blank data to trick shoutcast into clearing any previous # Default to blank data to trick shoutcast into clearing any previous
# values it might have. # values it might have.
taglist[gst.TAG_ARTIST] = u' ' taglist[gst.TAG_ARTIST] = ' '
taglist[gst.TAG_TITLE] = u' ' taglist[gst.TAG_TITLE] = ' '
taglist[gst.TAG_ALBUM] = u' ' taglist[gst.TAG_ALBUM] = ' '
if artists: if artists:
taglist[gst.TAG_ARTIST] = u', '.join([a.name for a in artists]) taglist[gst.TAG_ARTIST] = ', '.join([a.name for a in artists])
if track.name: if track.name:
taglist[gst.TAG_TITLE] = track.name taglist[gst.TAG_TITLE] = track.name

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import pykka import pykka

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import pygst import pygst
pygst.require('0.10') pygst.require('0.10')
import gst import gst

View File

@ -12,6 +12,8 @@ This is Mopidy's default mixer.
to ``autoaudiomixer`` to use this mixer. to ``autoaudiomixer`` to use this mixer.
""" """
from __future__ import unicode_literals
import pygst import pygst
pygst.require('0.10') pygst.require('0.10')
import gst import gst

View File

@ -9,6 +9,8 @@
- Set :attr:`mopidy.settings.MIXER` to ``fakemixer`` to use this mixer. - Set :attr:`mopidy.settings.MIXER` to ``fakemixer`` to use this mixer.
""" """
from __future__ import unicode_literals
import pygst import pygst
pygst.require('0.10') pygst.require('0.10')
import gobject import gobject

View File

@ -45,6 +45,8 @@ Configuration examples::
u'source=aux speakers-a=on speakers-b=off') u'source=aux speakers-a=on speakers-b=off')
""" """
from __future__ import unicode_literals
import logging import logging
import pygst import pygst
@ -107,7 +109,7 @@ class NadMixer(gst.Element, gst.ImplementsInterface, gst.interfaces.Mixer):
def do_change_state(self, transition): def do_change_state(self, transition):
if transition == gst.STATE_CHANGE_NULL_TO_READY: if transition == gst.STATE_CHANGE_NULL_TO_READY:
if serial is None: if serial is None:
logger.warning(u'nadmixer dependency python-serial not found') logger.warning('nadmixer dependency python-serial not found')
return gst.STATE_CHANGE_FAILURE return gst.STATE_CHANGE_FAILURE
self._start_nad_talker() self._start_nad_talker()
return gst.STATE_CHANGE_SUCCESS return gst.STATE_CHANGE_SUCCESS
@ -164,7 +166,7 @@ class NadTalker(pykka.ThreadingActor):
self._set_device_to_known_state() self._set_device_to_known_state()
def _open_connection(self): def _open_connection(self):
logger.info(u'NAD amplifier: Connecting through "%s"', self.port) logger.info('NAD amplifier: Connecting through "%s"', self.port)
self._device = serial.Serial( self._device = serial.Serial(
port=self.port, port=self.port,
baudrate=self.BAUDRATE, baudrate=self.BAUDRATE,
@ -183,7 +185,7 @@ class NadTalker(pykka.ThreadingActor):
def _get_device_model(self): def _get_device_model(self):
model = self._ask_device('Main.Model') model = self._ask_device('Main.Model')
logger.info(u'NAD amplifier: Connected to model "%s"', model) logger.info('NAD amplifier: Connected to model "%s"', model)
return model return model
def _power_device_on(self): def _power_device_on(self):
@ -212,19 +214,19 @@ class NadTalker(pykka.ThreadingActor):
if current_nad_volume is None: if current_nad_volume is None:
current_nad_volume = self.VOLUME_LEVELS current_nad_volume = self.VOLUME_LEVELS
if current_nad_volume == self.VOLUME_LEVELS: if current_nad_volume == self.VOLUME_LEVELS:
logger.info(u'NAD amplifier: Calibrating by setting volume to 0') logger.info('NAD amplifier: Calibrating by setting volume to 0')
self._nad_volume = current_nad_volume self._nad_volume = current_nad_volume
if self._decrease_volume(): if self._decrease_volume():
current_nad_volume -= 1 current_nad_volume -= 1
if current_nad_volume == 0: if current_nad_volume == 0:
logger.info(u'NAD amplifier: Done calibrating') logger.info('NAD amplifier: Done calibrating')
else: else:
self.actor_ref.proxy().calibrate_volume(current_nad_volume) self.actor_ref.proxy().calibrate_volume(current_nad_volume)
def set_volume(self, volume): def set_volume(self, volume):
# Increase or decrease the amplifier volume until it matches the given # Increase or decrease the amplifier volume until it matches the given
# target volume. # target volume.
logger.debug(u'Setting volume to %d' % volume) logger.debug('Setting volume to %d' % volume)
target_nad_volume = int(round(volume * self.VOLUME_LEVELS / 100.0)) target_nad_volume = int(round(volume * self.VOLUME_LEVELS / 100.0))
if self._nad_volume is None: if self._nad_volume is None:
return # Calibration needed return # Calibration needed
@ -250,12 +252,12 @@ class NadTalker(pykka.ThreadingActor):
if self._ask_device(key) == value: if self._ask_device(key) == value:
return return
logger.info( logger.info(
u'NAD amplifier: Setting "%s" to "%s" (attempt %d/3)', 'NAD amplifier: Setting "%s" to "%s" (attempt %d/3)',
key, value, attempt) key, value, attempt)
self._command_device(key, value) self._command_device(key, value)
if self._ask_device(key) != value: if self._ask_device(key) != value:
logger.info( logger.info(
u'NAD amplifier: Gave up on setting "%s" to "%s"', 'NAD amplifier: Gave up on setting "%s" to "%s"',
key, value) key, value)
def _ask_device(self, key): def _ask_device(self, key):

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import pygst import pygst
pygst.require('0.10') pygst.require('0.10')
import gst import gst

View File

@ -0,0 +1 @@
from __future__ import unicode_literals

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import copy import copy

View File

@ -14,6 +14,8 @@ The backend handles URIs starting with ``dummy:``.
- None - None
""" """
from __future__ import unicode_literals
import pykka import pykka
from mopidy.backends import base from mopidy.backends import base
@ -28,7 +30,7 @@ class DummyBackend(pykka.ThreadingActor, base.Backend):
self.playback = DummyPlaybackProvider(audio=audio, backend=self) self.playback = DummyPlaybackProvider(audio=audio, backend=self)
self.stored_playlists = DummyStoredPlaylistsProvider(backend=self) self.stored_playlists = DummyStoredPlaylistsProvider(backend=self)
self.uri_schemes = [u'dummy'] self.uri_schemes = ['dummy']
class DummyLibraryProvider(base.BaseLibraryProvider): class DummyLibraryProvider(base.BaseLibraryProvider):

View File

@ -20,5 +20,7 @@ https://github.com/mopidy/mopidy/issues?labels=Local+backend
- :attr:`mopidy.settings.LOCAL_TAG_CACHE_FILE` - :attr:`mopidy.settings.LOCAL_TAG_CACHE_FILE`
""" """
from __future__ import unicode_literals
# flake8: noqa # flake8: noqa
from .actor import LocalBackend from .actor import LocalBackend

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
import pykka import pykka
@ -7,7 +9,7 @@ from mopidy.backends import base
from .library import LocalLibraryProvider from .library import LocalLibraryProvider
from .stored_playlists import LocalStoredPlaylistsProvider from .stored_playlists import LocalStoredPlaylistsProvider
logger = logging.getLogger(u'mopidy.backends.local') logger = logging.getLogger('mopidy.backends.local')
class LocalBackend(pykka.ThreadingActor, base.Backend): class LocalBackend(pykka.ThreadingActor, base.Backend):
@ -18,4 +20,4 @@ class LocalBackend(pykka.ThreadingActor, base.Backend):
self.playback = base.BasePlaybackProvider(audio=audio, backend=self) self.playback = base.BasePlaybackProvider(audio=audio, backend=self)
self.stored_playlists = LocalStoredPlaylistsProvider(backend=self) self.stored_playlists = LocalStoredPlaylistsProvider(backend=self)
self.uri_schemes = [u'file'] self.uri_schemes = ['file']

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
from mopidy import settings from mopidy import settings
@ -6,7 +8,7 @@ from mopidy.models import Playlist, Album
from .translator import parse_mpd_tag_cache from .translator import parse_mpd_tag_cache
logger = logging.getLogger(u'mopidy.backends.local') logger = logging.getLogger('mopidy.backends.local')
class LocalLibraryProvider(base.BaseLibraryProvider): class LocalLibraryProvider(base.BaseLibraryProvider):
@ -30,7 +32,7 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
try: try:
return self._uri_mapping[uri] return self._uri_mapping[uri]
except KeyError: except KeyError:
logger.debug(u'Failed to lookup %r', uri) logger.debug('Failed to lookup %r', uri)
return None return None
def find_exact(self, **query): def find_exact(self, **query):

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import glob import glob
import logging import logging
import os import os
@ -11,7 +13,7 @@ from mopidy.utils import formatting, path
from .translator import parse_m3u from .translator import parse_m3u
logger = logging.getLogger(u'mopidy.backends.local') logger = logging.getLogger('mopidy.backends.local')
class LocalStoredPlaylistsProvider(base.BaseStoredPlaylistsProvider): class LocalStoredPlaylistsProvider(base.BaseStoredPlaylistsProvider):

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
from mopidy.models import Track, Artist, Album from mopidy.models import Track, Artist, Album
@ -68,19 +70,19 @@ def parse_mpd_tag_cache(tag_cache, music_dir=''):
current = {} current = {}
state = None state = None
for line in contents.split('\n'): for line in contents.split(b'\n'):
if line == 'songList begin': if line == b'songList begin':
state = 'songs' state = 'songs'
continue continue
elif line == 'songList end': elif line == b'songList end':
state = None state = None
continue continue
elif not state: elif not state:
continue continue
key, value = line.split(': ', 1) key, value = line.split(b': ', 1)
if key == 'key': if key == b'key':
_convert_mpd_data(current, tracks, music_dir) _convert_mpd_data(current, tracks, music_dir)
current.clear() current.clear()

View File

@ -30,5 +30,7 @@ https://github.com/mopidy/mopidy/issues?labels=Spotify+backend
- :attr:`mopidy.settings.SPOTIFY_PASSWORD` - :attr:`mopidy.settings.SPOTIFY_PASSWORD`
""" """
from __future__ import unicode_literals
# flake8: noqa # flake8: noqa
from .actor import SpotifyBackend from .actor import SpotifyBackend

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
import pykka import pykka
@ -24,7 +26,7 @@ class SpotifyBackend(pykka.ThreadingActor, base.Backend):
self.playback = SpotifyPlaybackProvider(audio=audio, backend=self) self.playback = SpotifyPlaybackProvider(audio=audio, backend=self)
self.stored_playlists = SpotifyStoredPlaylistsProvider(backend=self) self.stored_playlists = SpotifyStoredPlaylistsProvider(backend=self)
self.uri_schemes = [u'spotify'] self.uri_schemes = ['spotify']
# Fail early if settings are not present # Fail early if settings are not present
username = settings.SPOTIFY_USERNAME username = settings.SPOTIFY_USERNAME
@ -34,8 +36,8 @@ class SpotifyBackend(pykka.ThreadingActor, base.Backend):
username, password, audio=audio, backend_ref=self.actor_ref) username, password, audio=audio, backend_ref=self.actor_ref)
def on_start(self): def on_start(self):
logger.info(u'Mopidy uses SPOTIFY(R) CORE') logger.info('Mopidy uses SPOTIFY(R) CORE')
logger.debug(u'Connecting to Spotify') logger.debug('Connecting to Spotify')
self.spotify.start() self.spotify.start()
def on_stop(self): def on_stop(self):

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
from spotify.manager import SpotifyContainerManager as \ from spotify.manager import SpotifyContainerManager as \
@ -13,7 +15,7 @@ class SpotifyContainerManager(PyspotifyContainerManager):
def container_loaded(self, container, userdata): def container_loaded(self, container, userdata):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug(u'Callback called: playlist container loaded') logger.debug('Callback called: playlist container loaded')
self.session_manager.refresh_stored_playlists() self.session_manager.refresh_stored_playlists()
@ -22,12 +24,12 @@ class SpotifyContainerManager(PyspotifyContainerManager):
if playlist.type() == 'playlist': if playlist.type() == 'playlist':
self.session_manager.playlist_manager.watch(playlist) self.session_manager.playlist_manager.watch(playlist)
count += 1 count += 1
logger.debug(u'Watching %d playlist(s) for changes', count) logger.debug('Watching %d playlist(s) for changes', count)
def playlist_added(self, container, playlist, position, userdata): def playlist_added(self, container, playlist, position, userdata):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug( logger.debug(
u'Callback called: playlist added at position %d', position) 'Callback called: playlist added at position %d', position)
# container_loaded() is called after this callback, so we do not need # container_loaded() is called after this callback, so we do not need
# to handle this callback. # to handle this callback.
@ -35,7 +37,7 @@ class SpotifyContainerManager(PyspotifyContainerManager):
userdata): userdata):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug( logger.debug(
u'Callback called: playlist "%s" moved from position %d to %d', 'Callback called: playlist "%s" moved from position %d to %d',
playlist.name(), old_position, new_position) playlist.name(), old_position, new_position)
# container_loaded() is called after this callback, so we do not need # container_loaded() is called after this callback, so we do not need
# to handle this callback. # to handle this callback.
@ -43,7 +45,7 @@ class SpotifyContainerManager(PyspotifyContainerManager):
def playlist_removed(self, container, playlist, position, userdata): def playlist_removed(self, container, playlist, position, userdata):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug( logger.debug(
u'Callback called: playlist "%s" removed from position %d', 'Callback called: playlist "%s" removed from position %d',
playlist.name(), position) playlist.name(), position)
# container_loaded() is called after this callback, so we do not need # container_loaded() is called after this callback, so we do not need
# to handle this callback. # to handle this callback.

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
import Queue import Queue
@ -16,7 +18,7 @@ class SpotifyTrack(Track):
def __init__(self, uri): def __init__(self, uri):
super(SpotifyTrack, self).__init__() super(SpotifyTrack, self).__init__()
self._spotify_track = Link.from_string(uri).as_track() self._spotify_track = Link.from_string(uri).as_track()
self._unloaded_track = Track(uri=uri, name=u'[loading...]') self._unloaded_track = Track(uri=uri, name='[loading...]')
self._track = None self._track = None
@property @property
@ -57,7 +59,7 @@ class SpotifyLibraryProvider(base.BaseLibraryProvider):
try: try:
return SpotifyTrack(uri) return SpotifyTrack(uri)
except SpotifyError as e: except SpotifyError as e:
logger.debug(u'Failed to lookup "%s": %s', uri, e) logger.debug('Failed to lookup "%s": %s', uri, e)
return None return None
def refresh(self, uri=None): def refresh(self, uri=None):
@ -73,22 +75,22 @@ class SpotifyLibraryProvider(base.BaseLibraryProvider):
return Playlist(tracks=tracks) return Playlist(tracks=tracks)
spotify_query = [] spotify_query = []
for (field, values) in query.iteritems(): for (field, values) in query.iteritems():
if field == u'track': if field == 'track':
field = u'title' field = 'title'
if field == u'date': if field == 'date':
field = u'year' field = 'year'
if not hasattr(values, '__iter__'): if not hasattr(values, '__iter__'):
values = [values] values = [values]
for value in values: for value in values:
if field == u'any': if field == 'any':
spotify_query.append(value) spotify_query.append(value)
elif field == u'year': elif field == 'year':
value = int(value.split('-')[0]) # Extract year value = int(value.split('-')[0]) # Extract year
spotify_query.append(u'%s:%d' % (field, value)) spotify_query.append('%s:%d' % (field, value))
else: else:
spotify_query.append(u'%s:"%s"' % (field, value)) spotify_query.append('%s:"%s"' % (field, value))
spotify_query = u' '.join(spotify_query) spotify_query = ' '.join(spotify_query)
logger.debug(u'Spotify search query: %s' % spotify_query) logger.debug('Spotify search query: %s' % spotify_query)
queue = Queue.Queue() queue = Queue.Queue()
self.backend.spotify.search(spotify_query, queue) self.backend.spotify.search(spotify_query, queue)
try: try:

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
import time import time

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import datetime import datetime
import logging import logging
@ -14,90 +16,90 @@ class SpotifyPlaylistManager(PyspotifyPlaylistManager):
def tracks_added(self, playlist, tracks, position, userdata): def tracks_added(self, playlist, tracks, position, userdata):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug( logger.debug(
u'Callback called: ' 'Callback called: '
u'%d track(s) added to position %d in playlist "%s"', '%d track(s) added to position %d in playlist "%s"',
len(tracks), position, playlist.name()) len(tracks), position, playlist.name())
self.session_manager.refresh_stored_playlists() self.session_manager.refresh_stored_playlists()
def tracks_moved(self, playlist, tracks, new_position, userdata): def tracks_moved(self, playlist, tracks, new_position, userdata):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug( logger.debug(
u'Callback called: ' 'Callback called: '
u'%d track(s) moved to position %d in playlist "%s"', '%d track(s) moved to position %d in playlist "%s"',
len(tracks), new_position, playlist.name()) len(tracks), new_position, playlist.name())
self.session_manager.refresh_stored_playlists() self.session_manager.refresh_stored_playlists()
def tracks_removed(self, playlist, tracks, userdata): def tracks_removed(self, playlist, tracks, userdata):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug( logger.debug(
u'Callback called: ' 'Callback called: '
u'%d track(s) removed from playlist "%s"', '%d track(s) removed from playlist "%s"',
len(tracks), playlist.name()) len(tracks), playlist.name())
self.session_manager.refresh_stored_playlists() self.session_manager.refresh_stored_playlists()
def playlist_renamed(self, playlist, userdata): def playlist_renamed(self, playlist, userdata):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug( logger.debug(
u'Callback called: Playlist renamed to "%s"', playlist.name()) 'Callback called: Playlist renamed to "%s"', playlist.name())
self.session_manager.refresh_stored_playlists() self.session_manager.refresh_stored_playlists()
def playlist_state_changed(self, playlist, userdata): def playlist_state_changed(self, playlist, userdata):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug( logger.debug(
u'Callback called: The state of playlist "%s" changed', 'Callback called: The state of playlist "%s" changed',
playlist.name()) playlist.name())
def playlist_update_in_progress(self, playlist, done, userdata): def playlist_update_in_progress(self, playlist, done, userdata):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
if done: if done:
logger.debug( logger.debug(
u'Callback called: Update of playlist "%s" done', 'Callback called: Update of playlist "%s" done',
playlist.name()) playlist.name())
else: else:
logger.debug( logger.debug(
u'Callback called: Update of playlist "%s" in progress', 'Callback called: Update of playlist "%s" in progress',
playlist.name()) playlist.name())
def playlist_metadata_updated(self, playlist, userdata): def playlist_metadata_updated(self, playlist, userdata):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug( logger.debug(
u'Callback called: Metadata updated for playlist "%s"', 'Callback called: Metadata updated for playlist "%s"',
playlist.name()) playlist.name())
def track_created_changed(self, playlist, position, user, when, userdata): def track_created_changed(self, playlist, position, user, when, userdata):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
when = datetime.datetime.fromtimestamp(when) when = datetime.datetime.fromtimestamp(when)
logger.debug( logger.debug(
u'Callback called: Created by/when for track %d in playlist ' 'Callback called: Created by/when for track %d in playlist '
u'"%s" changed to user "N/A" and time "%s"', '"%s" changed to user "N/A" and time "%s"',
position, playlist.name(), when) position, playlist.name(), when)
def track_message_changed(self, playlist, position, message, userdata): def track_message_changed(self, playlist, position, message, userdata):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug( logger.debug(
u'Callback called: Message for track %d in playlist ' 'Callback called: Message for track %d in playlist '
u'"%s" changed to "%s"', position, playlist.name(), message) '"%s" changed to "%s"', position, playlist.name(), message)
def track_seen_changed(self, playlist, position, seen, userdata): def track_seen_changed(self, playlist, position, seen, userdata):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug( logger.debug(
u'Callback called: Seen attribute for track %d in playlist ' 'Callback called: Seen attribute for track %d in playlist '
u'"%s" changed to "%s"', position, playlist.name(), seen) '"%s" changed to "%s"', position, playlist.name(), seen)
def description_changed(self, playlist, description, userdata): def description_changed(self, playlist, description, userdata):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug( logger.debug(
u'Callback called: Description changed for playlist "%s" to "%s"', 'Callback called: Description changed for playlist "%s" to "%s"',
playlist.name(), description) playlist.name(), description)
def subscribers_changed(self, playlist, userdata): def subscribers_changed(self, playlist, userdata):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug( logger.debug(
u'Callback called: Subscribers changed for playlist "%s"', 'Callback called: Subscribers changed for playlist "%s"',
playlist.name()) playlist.name())
def image_changed(self, playlist, image, userdata): def image_changed(self, playlist, image, userdata):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug( logger.debug(
u'Callback called: Image changed for playlist "%s"', 'Callback called: Image changed for playlist "%s"',
playlist.name()) playlist.name())

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
import os import os
import threading import threading
@ -50,14 +52,14 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager):
def logged_in(self, session, error): def logged_in(self, session, error):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
if error: if error:
logger.error(u'Spotify login error: %s', error) logger.error('Spotify login error: %s', error)
return return
logger.info(u'Connected to Spotify') logger.info('Connected to Spotify')
self.session = session self.session = session
logger.debug( logger.debug(
u'Preferred Spotify bitrate is %s kbps', 'Preferred Spotify bitrate is %s kbps',
settings.SPOTIFY_BITRATE) settings.SPOTIFY_BITRATE)
self.session.set_preferred_bitrate(BITRATES[settings.SPOTIFY_BITRATE]) self.session.set_preferred_bitrate(BITRATES[settings.SPOTIFY_BITRATE])
@ -70,30 +72,30 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager):
def logged_out(self, session): def logged_out(self, session):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.info(u'Disconnected from Spotify') logger.info('Disconnected from Spotify')
def metadata_updated(self, session): def metadata_updated(self, session):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug(u'Callback called: Metadata updated') logger.debug('Callback called: Metadata updated')
def connection_error(self, session, error): def connection_error(self, session, error):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
if error is None: if error is None:
logger.info(u'Spotify connection OK') logger.info('Spotify connection OK')
else: else:
logger.error(u'Spotify connection error: %s', error) logger.error('Spotify connection error: %s', error)
self.backend.playback.pause() self.backend.playback.pause()
def message_to_user(self, session, message): def message_to_user(self, session, message):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug(u'User message: %s', message.strip()) logger.debug('User message: %s', message.strip())
def music_delivery(self, session, frames, frame_size, num_frames, def music_delivery(self, session, frames, frame_size, num_frames,
sample_type, sample_rate, channels): sample_type, sample_rate, channels):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
# pylint: disable = R0913 # pylint: disable = R0913
# Too many arguments (8/5) # Too many arguments (8/5)
assert sample_type == 0, u'Expects 16-bit signed integer samples' assert sample_type == 0, 'Expects 16-bit signed integer samples'
capabilites = """ capabilites = """
audio/x-raw-int, audio/x-raw-int,
endianness=(int)1234, endianness=(int)1234,
@ -111,12 +113,12 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager):
def play_token_lost(self, session): def play_token_lost(self, session):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug(u'Play token lost') logger.debug('Play token lost')
self.backend.playback.pause() self.backend.playback.pause()
def log_message(self, session, data): def log_message(self, session, data):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug(u'System message: %s' % data.strip()) logger.debug('System message: %s' % data.strip())
if 'offline-mgr' in data and 'files unlocked' in data: if 'offline-mgr' in data and 'files unlocked' in data:
# XXX This is a very very fragile and ugly hack, but we get no # XXX This is a very very fragile and ugly hack, but we get no
# proper event when libspotify is done with initial data loading. # proper event when libspotify is done with initial data loading.
@ -131,20 +133,20 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager):
def end_of_track(self, session): def end_of_track(self, session):
"""Callback used by pyspotify""" """Callback used by pyspotify"""
logger.debug(u'End of data stream reached') logger.debug('End of data stream reached')
self.audio.emit_end_of_stream() self.audio.emit_end_of_stream()
def refresh_stored_playlists(self): def refresh_stored_playlists(self):
"""Refresh the stored playlists in the backend with fresh meta data """Refresh the stored playlists in the backend with fresh meta data
from Spotify""" from Spotify"""
if not self._initial_data_receive_completed: if not self._initial_data_receive_completed:
logger.debug(u'Still getting data; skipped refresh of playlists') logger.debug('Still getting data; skipped refresh of playlists')
return return
playlists = map( playlists = map(
translator.to_mopidy_playlist, self.session.playlist_container()) translator.to_mopidy_playlist, self.session.playlist_container())
playlists = filter(None, playlists) playlists = filter(None, playlists)
self.backend.stored_playlists.playlists = playlists self.backend.stored_playlists.playlists = playlists
logger.info(u'Loaded %d Spotify playlist(s)', len(playlists)) logger.info('Loaded %d Spotify playlist(s)', len(playlists))
def search(self, query, queue): def search(self, query, queue):
"""Search method used by Mopidy backend""" """Search method used by Mopidy backend"""
@ -161,6 +163,6 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager):
def logout(self): def logout(self):
"""Log out from spotify""" """Log out from spotify"""
logger.debug(u'Logging out from Spotify') logger.debug('Logging out from Spotify')
if self.session: if self.session:
self.session.logout() self.session.logout()

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy.backends import base from mopidy.backends import base

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from spotify import Link from spotify import Link
from mopidy import settings from mopidy import settings
@ -9,7 +11,7 @@ def to_mopidy_artist(spotify_artist):
return return
uri = str(Link.from_artist(spotify_artist)) uri = str(Link.from_artist(spotify_artist))
if not spotify_artist.is_loaded(): if not spotify_artist.is_loaded():
return Artist(uri=uri, name=u'[loading...]') return Artist(uri=uri, name='[loading...]')
return Artist(uri=uri, name=spotify_artist.name()) return Artist(uri=uri, name=spotify_artist.name())
@ -18,7 +20,7 @@ def to_mopidy_album(spotify_album):
return return
uri = str(Link.from_album(spotify_album)) uri = str(Link.from_album(spotify_album))
if not spotify_album.is_loaded(): if not spotify_album.is_loaded():
return Album(uri=uri, name=u'[loading...]') return Album(uri=uri, name='[loading...]')
return Album( return Album(
uri=uri, uri=uri,
name=spotify_album.name(), name=spotify_album.name(),
@ -31,7 +33,7 @@ def to_mopidy_track(spotify_track):
return return
uri = str(Link.from_track(spotify_track, 0)) uri = str(Link.from_track(spotify_track, 0))
if not spotify_track.is_loaded(): if not spotify_track.is_loaded():
return Track(uri=uri, name=u'[loading...]') return Track(uri=uri, name='[loading...]')
spotify_album = spotify_track.album() spotify_album = spotify_track.album()
if spotify_album is not None and spotify_album.is_loaded(): if spotify_album is not None and spotify_album.is_loaded():
date = spotify_album.year() date = spotify_album.year()
@ -53,7 +55,7 @@ def to_mopidy_playlist(spotify_playlist):
return return
uri = str(Link.from_playlist(spotify_playlist)) uri = str(Link.from_playlist(spotify_playlist))
if not spotify_playlist.is_loaded(): if not spotify_playlist.is_loaded():
return Playlist(uri=uri, name=u'[loading...]') return Playlist(uri=uri, name='[loading...]')
return Playlist( return Playlist(
uri=uri, uri=uri,
name=spotify_playlist.name(), name=spotify_playlist.name(),

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
# flake8: noqa # flake8: noqa
from .actor import Core from .actor import Core
from .current_playlist import CurrentPlaylistController from .current_playlist import CurrentPlaylistController

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import itertools import itertools
import pykka import pykka

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from copy import copy from copy import copy
import logging import logging
import random import random
@ -73,7 +75,7 @@ class CurrentPlaylistController(object):
was added to the current playlist playlist was added to the current playlist playlist
""" """
assert at_position <= len(self._cp_tracks), \ assert at_position <= len(self._cp_tracks), \
u'at_position can not be greater than playlist length' 'at_position can not be greater than playlist length'
cp_track = CpTrack(self.cp_id, track) cp_track = CpTrack(self.cp_id, track)
if at_position is not None: if at_position is not None:
self._cp_tracks.insert(at_position, cp_track) self._cp_tracks.insert(at_position, cp_track)
@ -132,9 +134,9 @@ class CurrentPlaylistController(object):
criteria_string = ', '.join( criteria_string = ', '.join(
['%s=%s' % (k, v) for (k, v) in criteria.iteritems()]) ['%s=%s' % (k, v) for (k, v) in criteria.iteritems()])
if len(matches) == 0: if len(matches) == 0:
raise LookupError(u'"%s" match no tracks' % criteria_string) raise LookupError('"%s" match no tracks' % criteria_string)
else: else:
raise LookupError(u'"%s" match multiple tracks' % criteria_string) raise LookupError('"%s" match multiple tracks' % criteria_string)
def index(self, cp_track): def index(self, cp_track):
""" """
@ -237,5 +239,5 @@ class CurrentPlaylistController(object):
return [copy(cp_track) for cp_track in self._cp_tracks[start:end]] return [copy(cp_track) for cp_track in self._cp_tracks[start:end]]
def _trigger_playlist_changed(self): def _trigger_playlist_changed(self):
logger.debug(u'Triggering playlist changed event') logger.debug('Triggering playlist changed event')
listener.CoreListener.send('playlist_changed') listener.CoreListener.send('playlist_changed')

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import itertools import itertools
import urlparse import urlparse

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import pykka import pykka

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
import random import random
import urlparse import urlparse
@ -28,13 +30,13 @@ class PlaybackState(object):
""" """
#: Constant representing the paused state. #: Constant representing the paused state.
PAUSED = u'paused' PAUSED = 'paused'
#: Constant representing the playing state. #: Constant representing the playing state.
PLAYING = u'playing' PLAYING = 'playing'
#: Constant representing the stopped state. #: Constant representing the stopped state.
STOPPED = u'stopped' STOPPED = 'stopped'
class PlaybackController(object): class PlaybackController(object):
@ -290,7 +292,7 @@ class PlaybackController(object):
@state.setter # noqa @state.setter # noqa
def state(self, new_state): def state(self, new_state):
(old_state, self._state) = (self.state, new_state) (old_state, self._state) = (self.state, new_state)
logger.debug(u'Changing state: %s -> %s', old_state, new_state) logger.debug('Changing state: %s -> %s', old_state, new_state)
self._trigger_playback_state_changed(old_state, new_state) self._trigger_playback_state_changed(old_state, new_state)
@ -493,7 +495,7 @@ class PlaybackController(object):
self.current_cp_track = None self.current_cp_track = None
def _trigger_track_playback_paused(self): def _trigger_track_playback_paused(self):
logger.debug(u'Triggering track playback paused event') logger.debug('Triggering track playback paused event')
if self.current_track is None: if self.current_track is None:
return return
listener.CoreListener.send( listener.CoreListener.send(
@ -501,7 +503,7 @@ class PlaybackController(object):
track=self.current_track, time_position=self.time_position) track=self.current_track, time_position=self.time_position)
def _trigger_track_playback_resumed(self): def _trigger_track_playback_resumed(self):
logger.debug(u'Triggering track playback resumed event') logger.debug('Triggering track playback resumed event')
if self.current_track is None: if self.current_track is None:
return return
listener.CoreListener.send( listener.CoreListener.send(
@ -509,14 +511,14 @@ class PlaybackController(object):
track=self.current_track, time_position=self.time_position) track=self.current_track, time_position=self.time_position)
def _trigger_track_playback_started(self): def _trigger_track_playback_started(self):
logger.debug(u'Triggering track playback started event') logger.debug('Triggering track playback started event')
if self.current_track is None: if self.current_track is None:
return return
listener.CoreListener.send( listener.CoreListener.send(
'track_playback_started', track=self.current_track) 'track_playback_started', track=self.current_track)
def _trigger_track_playback_ended(self): def _trigger_track_playback_ended(self):
logger.debug(u'Triggering track playback ended event') logger.debug('Triggering track playback ended event')
if self.current_track is None: if self.current_track is None:
return return
listener.CoreListener.send( listener.CoreListener.send(
@ -524,15 +526,15 @@ class PlaybackController(object):
track=self.current_track, time_position=self.time_position) track=self.current_track, time_position=self.time_position)
def _trigger_playback_state_changed(self, old_state, new_state): def _trigger_playback_state_changed(self, old_state, new_state):
logger.debug(u'Triggering playback state change event') logger.debug('Triggering playback state change event')
listener.CoreListener.send( listener.CoreListener.send(
'playback_state_changed', 'playback_state_changed',
old_state=old_state, new_state=new_state) old_state=old_state, new_state=new_state)
def _trigger_options_changed(self): def _trigger_options_changed(self):
logger.debug(u'Triggering options changed event') logger.debug('Triggering options changed event')
listener.CoreListener.send('options_changed') listener.CoreListener.send('options_changed')
def _trigger_seeked(self, time_position): def _trigger_seeked(self, time_position):
logger.debug(u'Triggering seeked event') logger.debug('Triggering seeked event')
listener.CoreListener.send('seeked', time_position=time_position) listener.CoreListener.send('seeked', time_position=time_position)

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import itertools import itertools
import urlparse import urlparse

View File

@ -1,3 +1,6 @@
from __future__ import unicode_literals
class MopidyException(Exception): class MopidyException(Exception):
def __init__(self, message, *args, **kwargs): def __init__(self, message, *args, **kwargs):
super(MopidyException, self).__init__(message, *args, **kwargs) super(MopidyException, self).__init__(message, *args, **kwargs)

View File

@ -0,0 +1 @@
from __future__ import unicode_literals

View File

@ -22,6 +22,8 @@ Make sure :attr:`mopidy.settings.FRONTENDS` includes
the Last.fm frontend. the Last.fm frontend.
""" """
from __future__ import unicode_literals
import logging import logging
import time import time
@ -54,21 +56,21 @@ class LastfmFrontend(pykka.ThreadingActor, CoreListener):
self.lastfm = pylast.LastFMNetwork( self.lastfm = pylast.LastFMNetwork(
api_key=API_KEY, api_secret=API_SECRET, api_key=API_KEY, api_secret=API_SECRET,
username=username, password_hash=password_hash) username=username, password_hash=password_hash)
logger.info(u'Connected to Last.fm') logger.info('Connected to Last.fm')
except exceptions.SettingsError as e: except exceptions.SettingsError as e:
logger.info(u'Last.fm scrobbler not started') logger.info('Last.fm scrobbler not started')
logger.debug(u'Last.fm settings error: %s', e) logger.debug('Last.fm settings error: %s', e)
self.stop() self.stop()
except (pylast.NetworkError, pylast.MalformedResponseError, except (pylast.NetworkError, pylast.MalformedResponseError,
pylast.WSError) as e: pylast.WSError) as e:
logger.error(u'Error during Last.fm setup: %s', e) logger.error('Error during Last.fm setup: %s', e)
self.stop() self.stop()
def track_playback_started(self, track): def track_playback_started(self, track):
artists = ', '.join([a.name for a in track.artists]) artists = ', '.join([a.name for a in track.artists])
duration = track.length and track.length // 1000 or 0 duration = track.length and track.length // 1000 or 0
self.last_start_time = int(time.time()) self.last_start_time = int(time.time())
logger.debug(u'Now playing track: %s - %s', artists, track.name) logger.debug('Now playing track: %s - %s', artists, track.name)
try: try:
self.lastfm.update_now_playing( self.lastfm.update_now_playing(
artists, artists,
@ -79,22 +81,22 @@ class LastfmFrontend(pykka.ThreadingActor, CoreListener):
mbid=(track.musicbrainz_id or '')) mbid=(track.musicbrainz_id or ''))
except (pylast.ScrobblingError, pylast.NetworkError, except (pylast.ScrobblingError, pylast.NetworkError,
pylast.MalformedResponseError, pylast.WSError) as e: pylast.MalformedResponseError, pylast.WSError) as e:
logger.warning(u'Error submitting playing track to Last.fm: %s', e) logger.warning('Error submitting playing track to Last.fm: %s', e)
def track_playback_ended(self, track, time_position): def track_playback_ended(self, track, time_position):
artists = ', '.join([a.name for a in track.artists]) artists = ', '.join([a.name for a in track.artists])
duration = track.length and track.length // 1000 or 0 duration = track.length and track.length // 1000 or 0
time_position = time_position // 1000 time_position = time_position // 1000
if duration < 30: if duration < 30:
logger.debug(u'Track too short to scrobble. (30s)') logger.debug('Track too short to scrobble. (30s)')
return return
if time_position < duration // 2 and time_position < 240: if time_position < duration // 2 and time_position < 240:
logger.debug( logger.debug(
u'Track not played long enough to scrobble. (50% or 240s)') 'Track not played long enough to scrobble. (50% or 240s)')
return return
if self.last_start_time is None: if self.last_start_time is None:
self.last_start_time = int(time.time()) - duration self.last_start_time = int(time.time()) - duration
logger.debug(u'Scrobbling track: %s - %s', artists, track.name) logger.debug('Scrobbling track: %s - %s', artists, track.name)
try: try:
self.lastfm.scrobble( self.lastfm.scrobble(
artists, artists,
@ -106,4 +108,4 @@ class LastfmFrontend(pykka.ThreadingActor, CoreListener):
mbid=(track.musicbrainz_id or '')) mbid=(track.musicbrainz_id or ''))
except (pylast.ScrobblingError, pylast.NetworkError, except (pylast.ScrobblingError, pylast.NetworkError,
pylast.MalformedResponseError, pylast.WSError) as e: pylast.MalformedResponseError, pylast.WSError) as e:
logger.warning(u'Error submitting played track to Last.fm: %s', e) logger.warning('Error submitting played track to Last.fm: %s', e)

View File

@ -21,5 +21,7 @@ Make sure :attr:`mopidy.settings.FRONTENDS` includes
frontend. frontend.
""" """
from __future__ import unicode_literals
# flake8: noqa # flake8: noqa
from .actor import MpdFrontend from .actor import MpdFrontend

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
import sys import sys
@ -24,11 +26,11 @@ class MpdFrontend(pykka.ThreadingActor, CoreListener):
max_connections=settings.MPD_SERVER_MAX_CONNECTIONS) max_connections=settings.MPD_SERVER_MAX_CONNECTIONS)
except IOError as error: except IOError as error:
logger.error( logger.error(
u'MPD server startup failed: %s', 'MPD server startup failed: %s',
encoding.locale_decode(error)) encoding.locale_decode(error))
sys.exit(1) sys.exit(1)
logger.info(u'MPD server running at [%s]:%s', hostname, port) logger.info('MPD server running at [%s]:%s', hostname, port)
def on_stop(self): def on_stop(self):
process.stop_actors_by_class(session.MpdSession) process.stop_actors_by_class(session.MpdSession)

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
import re import re
@ -52,8 +54,8 @@ class MpdDispatcher(object):
response = [] response = []
for subsystem in subsystems: for subsystem in subsystems:
response.append(u'changed: %s' % subsystem) response.append('changed: %s' % subsystem)
response.append(u'OK') response.append('OK')
self.context.subscriptions = set() self.context.subscriptions = set()
self.context.events = set() self.context.events = set()
self.context.session.send_lines(response) self.context.session.send_lines(response)
@ -103,26 +105,26 @@ class MpdDispatcher(object):
response = self._call_next_filter(request, response, filter_chain) response = self._call_next_filter(request, response, filter_chain)
if (self._is_receiving_command_list(request) or if (self._is_receiving_command_list(request) or
self._is_processing_command_list(request)): self._is_processing_command_list(request)):
if response and response[-1] == u'OK': if response and response[-1] == 'OK':
response = response[:-1] response = response[:-1]
return response return response
def _is_receiving_command_list(self, request): def _is_receiving_command_list(self, request):
return ( return (
self.command_list_receiving and request != u'command_list_end') self.command_list_receiving and request != 'command_list_end')
def _is_processing_command_list(self, request): def _is_processing_command_list(self, request):
return ( return (
self.command_list_index is not None and self.command_list_index is not None and
request != u'command_list_end') request != 'command_list_end')
### Filter: idle ### Filter: idle
def _idle_filter(self, request, response, filter_chain): def _idle_filter(self, request, response, filter_chain):
if self._is_currently_idle() and not self._noidle.match(request): if self._is_currently_idle() and not self._noidle.match(request):
logger.debug( logger.debug(
u'Client sent us %s, only %s is allowed while in ' 'Client sent us %s, only %s is allowed while in '
u'the idle state', repr(request), repr(u'noidle')) 'the idle state', repr(request), repr('noidle'))
self.context.session.close() self.context.session.close()
return [] return []
@ -144,11 +146,11 @@ class MpdDispatcher(object):
def _add_ok_filter(self, request, response, filter_chain): def _add_ok_filter(self, request, response, filter_chain):
response = self._call_next_filter(request, response, filter_chain) response = self._call_next_filter(request, response, filter_chain)
if not self._has_error(response): if not self._has_error(response):
response.append(u'OK') response.append('OK')
return response return response
def _has_error(self, response): def _has_error(self, response):
return response and response[-1].startswith(u'ACK') return response and response[-1].startswith('ACK')
### Filter: call handler ### Filter: call handler
@ -157,7 +159,7 @@ class MpdDispatcher(object):
response = self._format_response(self._call_handler(request)) response = self._format_response(self._call_handler(request))
return self._call_next_filter(request, response, filter_chain) return self._call_next_filter(request, response, filter_chain)
except pykka.ActorDeadError as e: except pykka.ActorDeadError as e:
logger.warning(u'Tried to communicate with dead actor.') logger.warning('Tried to communicate with dead actor.')
raise exceptions.MpdSystemError(e) raise exceptions.MpdSystemError(e)
def _call_handler(self, request): def _call_handler(self, request):
@ -173,7 +175,7 @@ class MpdDispatcher(object):
command_name = request.split(' ')[0] command_name = request.split(' ')[0]
if command_name in [command.name for command in protocol.mpd_commands]: if command_name in [command.name for command in protocol.mpd_commands]:
raise exceptions.MpdArgError( raise exceptions.MpdArgError(
u'incorrect arguments', command=command_name) 'incorrect arguments', command=command_name)
raise exceptions.MpdUnknownCommand(command=command_name) raise exceptions.MpdUnknownCommand(command=command_name)
def _format_response(self, response): def _format_response(self, response):
@ -202,10 +204,10 @@ class MpdDispatcher(object):
def _format_lines(self, line): def _format_lines(self, line):
if isinstance(line, dict): if isinstance(line, dict):
return [u'%s: %s' % (key, value) for (key, value) in line.items()] return ['%s: %s' % (key, value) for (key, value) in line.items()]
if isinstance(line, tuple): if isinstance(line, tuple):
(key, value) = line (key, value) = line
return [u'%s: %s' % (key, value)] return ['%s: %s' % (key, value)]
return [line] return [line]

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy.exceptions import MopidyException from mopidy.exceptions import MopidyException
@ -19,7 +21,7 @@ class MpdAckError(MopidyException):
error_code = 0 error_code = 0
def __init__(self, message=u'', index=0, command=u''): def __init__(self, message='', index=0, command=''):
super(MpdAckError, self).__init__(message, index, command) super(MpdAckError, self).__init__(message, index, command)
self.message = message self.message = message
self.index = index self.index = index
@ -31,7 +33,7 @@ class MpdAckError(MopidyException):
ACK [%(error_code)i@%(index)i] {%(command)s} description ACK [%(error_code)i@%(index)i] {%(command)s} description
""" """
return u'ACK [%i@%i] {%s} %s' % ( return 'ACK [%i@%i] {%s} %s' % (
self.__class__.error_code, self.index, self.command, self.message) self.__class__.error_code, self.index, self.command, self.message)
@ -48,7 +50,7 @@ class MpdPermissionError(MpdAckError):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(MpdPermissionError, self).__init__(*args, **kwargs) super(MpdPermissionError, self).__init__(*args, **kwargs)
self.message = u'you don\'t have permission for "%s"' % self.command self.message = 'you don\'t have permission for "%s"' % self.command
class MpdUnknownCommand(MpdAckError): class MpdUnknownCommand(MpdAckError):
@ -56,8 +58,8 @@ class MpdUnknownCommand(MpdAckError):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(MpdUnknownCommand, self).__init__(*args, **kwargs) super(MpdUnknownCommand, self).__init__(*args, **kwargs)
self.message = u'unknown command "%s"' % self.command self.message = 'unknown command "%s"' % self.command
self.command = u'' self.command = ''
class MpdNoExistError(MpdAckError): class MpdNoExistError(MpdAckError):
@ -73,4 +75,4 @@ class MpdNotImplemented(MpdAckError):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(MpdNotImplemented, self).__init__(*args, **kwargs) super(MpdNotImplemented, self).__init__(*args, **kwargs)
self.message = u'Not implemented' self.message = 'Not implemented'

View File

@ -10,17 +10,19 @@ implement our own MPD server which is compatible with the numerous existing
`MPD clients <http://mpd.wikia.com/wiki/Clients>`_. `MPD clients <http://mpd.wikia.com/wiki/Clients>`_.
""" """
from __future__ import unicode_literals
from collections import namedtuple from collections import namedtuple
import re import re
#: The MPD protocol uses UTF-8 for encoding all data. #: The MPD protocol uses UTF-8 for encoding all data.
ENCODING = u'UTF-8' ENCODING = 'UTF-8'
#: The MPD protocol uses ``\n`` as line terminator. #: The MPD protocol uses ``\n`` as line terminator.
LINE_TERMINATOR = u'\n' LINE_TERMINATOR = '\n'
#: The MPD protocol version is 0.16.0. #: The MPD protocol version is 0.16.0.
VERSION = u'0.16.0' VERSION = '0.16.0'
MpdCommand = namedtuple('MpdCommand', ['name', 'auth_required']) MpdCommand = namedtuple('MpdCommand', ['name', 'auth_required'])
@ -55,7 +57,7 @@ def handle_request(pattern, auth_required=True):
mpd_commands.add( mpd_commands.add(
MpdCommand(name=match.group(), auth_required=auth_required)) MpdCommand(name=match.group(), auth_required=auth_required))
if pattern in request_handlers: if pattern in request_handlers:
raise ValueError(u'Tried to redefine handler for %s with %s' % ( raise ValueError('Tried to redefine handler for %s with %s' % (
pattern, func)) pattern, func))
request_handlers[pattern] = func request_handlers[pattern] = func
func.__doc__ = ' - *Pattern:* ``%s``\n\n%s' % ( func.__doc__ = ' - *Pattern:* ``%s``\n\n%s' % (

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy.frontends.mpd.protocol import handle_request from mopidy.frontends.mpd.protocol import handle_request
from mopidy.frontends.mpd.exceptions import MpdNotImplemented from mopidy.frontends.mpd.exceptions import MpdNotImplemented

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy.frontends.mpd.protocol import handle_request from mopidy.frontends.mpd.protocol import handle_request
from mopidy.frontends.mpd.exceptions import MpdUnknownCommand from mopidy.frontends.mpd.exceptions import MpdUnknownCommand
@ -40,10 +42,10 @@ def command_list_end(context):
command, current_command_list_index=index) command, current_command_list_index=index)
command_list_response.extend(response) command_list_response.extend(response)
if (command_list_response and if (command_list_response and
command_list_response[-1].startswith(u'ACK')): command_list_response[-1].startswith('ACK')):
return command_list_response return command_list_response
if command_list_ok: if command_list_ok:
command_list_response.append(u'list_OK') command_list_response.append('list_OK')
return command_list_response return command_list_response

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy import settings from mopidy import settings
from mopidy.frontends.mpd.protocol import handle_request from mopidy.frontends.mpd.protocol import handle_request
from mopidy.frontends.mpd.exceptions import ( from mopidy.frontends.mpd.exceptions import (
@ -25,7 +27,7 @@ def kill(context):
Kills MPD. Kills MPD.
""" """
raise MpdPermissionError(command=u'kill') raise MpdPermissionError(command='kill')
@handle_request(r'^password "(?P<password>[^"]+)"$', auth_required=False) @handle_request(r'^password "(?P<password>[^"]+)"$', auth_required=False)
@ -41,7 +43,7 @@ def password_(context, password):
if password == settings.MPD_SERVER_PASSWORD: if password == settings.MPD_SERVER_PASSWORD:
context.dispatcher.authenticated = True context.dispatcher.authenticated = True
else: else:
raise MpdPasswordError(u'incorrect password', command=u'password') raise MpdPasswordError('incorrect password', command='password')
@handle_request(r'^ping$', auth_required=False) @handle_request(r'^ping$', auth_required=False)

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy.frontends.mpd import translator from mopidy.frontends.mpd import translator
from mopidy.frontends.mpd.exceptions import ( from mopidy.frontends.mpd.exceptions import (
MpdArgError, MpdNoExistError, MpdNotImplemented) MpdArgError, MpdNoExistError, MpdNotImplemented)
@ -26,8 +28,7 @@ def add(context, uri):
if track is not None: if track is not None:
context.core.current_playlist.add(track) context.core.current_playlist.add(track)
return return
raise MpdNoExistError( raise MpdNoExistError('directory or file not found', command='add')
u'directory or file not found', command=u'add')
@handle_request(r'^addid "(?P<uri>[^"]*)"( "(?P<songpos>\d+)")*$') @handle_request(r'^addid "(?P<uri>[^"]*)"( "(?P<songpos>\d+)")*$')
@ -50,14 +51,14 @@ def addid(context, uri, songpos=None):
- ``addid ""`` should return an error. - ``addid ""`` should return an error.
""" """
if not uri: if not uri:
raise MpdNoExistError(u'No such song', command=u'addid') raise MpdNoExistError('No such song', command='addid')
if songpos is not None: if songpos is not None:
songpos = int(songpos) songpos = int(songpos)
track = context.core.library.lookup(uri).get() track = context.core.library.lookup(uri).get()
if track is None: if track is None:
raise MpdNoExistError(u'No such song', command=u'addid') raise MpdNoExistError('No such song', command='addid')
if songpos and songpos > context.core.current_playlist.length.get(): if songpos and songpos > context.core.current_playlist.length.get():
raise MpdArgError(u'Bad song index', command=u'addid') raise MpdArgError('Bad song index', command='addid')
cp_track = context.core.current_playlist.add( cp_track = context.core.current_playlist.add(
track, at_position=songpos).get() track, at_position=songpos).get()
return ('Id', cp_track.cpid) return ('Id', cp_track.cpid)
@ -79,7 +80,7 @@ def delete_range(context, start, end=None):
end = context.core.current_playlist.length.get() end = context.core.current_playlist.length.get()
cp_tracks = context.core.current_playlist.slice(start, end).get() cp_tracks = context.core.current_playlist.slice(start, end).get()
if not cp_tracks: if not cp_tracks:
raise MpdArgError(u'Bad song index', command=u'delete') raise MpdArgError('Bad song index', command='delete')
for (cpid, _) in cp_tracks: for (cpid, _) in cp_tracks:
context.core.current_playlist.remove(cpid=cpid) context.core.current_playlist.remove(cpid=cpid)
@ -93,7 +94,7 @@ def delete_songpos(context, songpos):
songpos, songpos + 1).get()[0] songpos, songpos + 1).get()[0]
context.core.current_playlist.remove(cpid=cpid) context.core.current_playlist.remove(cpid=cpid)
except IndexError: except IndexError:
raise MpdArgError(u'Bad song index', command=u'delete') raise MpdArgError('Bad song index', command='delete')
@handle_request(r'^deleteid "(?P<cpid>\d+)"$') @handle_request(r'^deleteid "(?P<cpid>\d+)"$')
@ -111,7 +112,7 @@ def deleteid(context, cpid):
context.core.playback.next() context.core.playback.next()
return context.core.current_playlist.remove(cpid=cpid).get() return context.core.current_playlist.remove(cpid=cpid).get()
except LookupError: except LookupError:
raise MpdNoExistError(u'No such song', command=u'deleteid') raise MpdNoExistError('No such song', command='deleteid')
@handle_request(r'^clear$') @handle_request(r'^clear$')
@ -227,7 +228,7 @@ def playlistid(context, cpid=None):
position = context.core.current_playlist.index(cp_track).get() position = context.core.current_playlist.index(cp_track).get()
return translator.track_to_mpd_format(cp_track, position=position) return translator.track_to_mpd_format(cp_track, position=position)
except LookupError: except LookupError:
raise MpdNoExistError(u'No such song', command=u'playlistid') raise MpdNoExistError('No such song', command='playlistid')
else: else:
return translator.tracks_to_mpd_format( return translator.tracks_to_mpd_format(
context.core.current_playlist.cp_tracks.get()) context.core.current_playlist.cp_tracks.get())
@ -261,7 +262,7 @@ def playlistinfo(context, songpos=None, start=None, end=None):
start = 0 start = 0
start = int(start) start = int(start)
if not (0 <= start <= context.core.current_playlist.length.get()): if not (0 <= start <= context.core.current_playlist.length.get()):
raise MpdArgError(u'Bad song index', command=u'playlistinfo') raise MpdArgError('Bad song index', command='playlistinfo')
if end is not None: if end is not None:
end = int(end) end = int(end)
if end > context.core.current_playlist.length.get(): if end > context.core.current_playlist.length.get():
@ -331,8 +332,8 @@ def plchangesposid(context, version):
result = [] result = []
for (position, (cpid, _)) in enumerate( for (position, (cpid, _)) in enumerate(
context.core.current_playlist.cp_tracks.get()): context.core.current_playlist.cp_tracks.get()):
result.append((u'cpos', position)) result.append(('cpos', position))
result.append((u'Id', cpid)) result.append(('Id', cpid))
return result return result

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy.frontends.mpd.protocol import handle_request from mopidy.frontends.mpd.protocol import handle_request

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import re import re
import shlex import shlex
@ -20,8 +22,8 @@ def _build_query(mpd_query):
for query_part in query_parts: for query_part in query_parts:
m = re.match(query_part_pattern, query_part) m = re.match(query_part_pattern, query_part)
field = m.groupdict()['field'].lower() field = m.groupdict()['field'].lower()
if field == u'title': if field == 'title':
field = u'track' field = 'track'
field = str(field) # Needed for kwargs keys on OS X and Windows field = str(field) # Needed for kwargs keys on OS X and Windows
what = m.groupdict()['what'].lower() what = m.groupdict()['what'].lower()
if field in query: if field in query:
@ -183,13 +185,13 @@ def list_(context, field, mpd_query=None):
""" """
field = field.lower() field = field.lower()
query = _list_build_query(field, mpd_query) query = _list_build_query(field, mpd_query)
if field == u'artist': if field == 'artist':
return _list_artist(context, query) return _list_artist(context, query)
elif field == u'album': elif field == 'album':
return _list_album(context, query) return _list_album(context, query)
elif field == u'date': elif field == 'date':
return _list_date(context, query) return _list_date(context, query)
elif field == u'genre': elif field == 'genre':
pass # TODO We don't have genre in our internal data structures yet pass # TODO We don't have genre in our internal data structures yet
@ -202,16 +204,16 @@ def _list_build_query(field, mpd_query):
tokens = shlex.split(mpd_query.encode('utf-8')) tokens = shlex.split(mpd_query.encode('utf-8'))
except ValueError as error: except ValueError as error:
if str(error) == 'No closing quotation': if str(error) == 'No closing quotation':
raise MpdArgError(u'Invalid unquoted character', command=u'list') raise MpdArgError('Invalid unquoted character', command='list')
else: else:
raise raise
tokens = [t.decode('utf-8') for t in tokens] tokens = [t.decode('utf-8') for t in tokens]
if len(tokens) == 1: if len(tokens) == 1:
if field == u'album': if field == 'album':
return {'artist': [tokens[0]]} return {'artist': [tokens[0]]}
else: else:
raise MpdArgError( raise MpdArgError(
u'should be "Album" for 3 arguments', command=u'list') 'should be "Album" for 3 arguments', command='list')
elif len(tokens) % 2 == 0: elif len(tokens) % 2 == 0:
query = {} query = {}
while tokens: while tokens:
@ -219,15 +221,15 @@ def _list_build_query(field, mpd_query):
key = str(key) # Needed for kwargs keys on OS X and Windows key = str(key) # Needed for kwargs keys on OS X and Windows
value = tokens[1] value = tokens[1]
tokens = tokens[2:] tokens = tokens[2:]
if key not in (u'artist', u'album', u'date', u'genre'): if key not in ('artist', 'album', 'date', 'genre'):
raise MpdArgError(u'not able to parse args', command=u'list') raise MpdArgError('not able to parse args', command='list')
if key in query: if key in query:
query[key].append(value) query[key].append(value)
else: else:
query[key] = [value] query[key] = [value]
return query return query
else: else:
raise MpdArgError(u'not able to parse args', command=u'list') raise MpdArgError('not able to parse args', command='list')
def _list_artist(context, query): def _list_artist(context, query):
@ -235,7 +237,7 @@ def _list_artist(context, query):
playlist = context.core.library.find_exact(**query).get() playlist = context.core.library.find_exact(**query).get()
for track in playlist.tracks: for track in playlist.tracks:
for artist in track.artists: for artist in track.artists:
artists.add((u'Artist', artist.name)) artists.add(('Artist', artist.name))
return artists return artists
@ -244,7 +246,7 @@ def _list_album(context, query):
playlist = context.core.library.find_exact(**query).get() playlist = context.core.library.find_exact(**query).get()
for track in playlist.tracks: for track in playlist.tracks:
if track.album is not None: if track.album is not None:
albums.add((u'Album', track.album.name)) albums.add(('Album', track.album.name))
return albums return albums
@ -253,7 +255,7 @@ def _list_date(context, query):
playlist = context.core.library.find_exact(**query).get() playlist = context.core.library.find_exact(**query).get()
for track in playlist.tracks: for track in playlist.tracks:
if track.date is not None: if track.date is not None:
dates.add((u'Date', track.date)) dates.add(('Date', track.date))
return dates return dates
@ -300,7 +302,7 @@ def lsinfo(context, uri=None):
directories located at the root level, for both ``lsinfo``, ``lsinfo directories located at the root level, for both ``lsinfo``, ``lsinfo
""``, and ``lsinfo "/"``. ""``, and ``lsinfo "/"``.
""" """
if uri is None or uri == u'/' or uri == u'': if uri is None or uri == '/' or uri == '':
return stored_playlists.listplaylists(context) return stored_playlists.listplaylists(context)
raise MpdNotImplemented # TODO raise MpdNotImplemented # TODO

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy.core import PlaybackState from mopidy.core import PlaybackState
from mopidy.frontends.mpd.protocol import handle_request from mopidy.frontends.mpd.protocol import handle_request
from mopidy.frontends.mpd.exceptions import ( from mopidy.frontends.mpd.exceptions import (
@ -153,7 +155,7 @@ def playid(context, cpid):
cp_track = context.core.current_playlist.get(cpid=cpid).get() cp_track = context.core.current_playlist.get(cpid=cpid).get()
return context.core.playback.play(cp_track).get() return context.core.playback.play(cp_track).get()
except LookupError: except LookupError:
raise MpdNoExistError(u'No such song', command=u'playid') raise MpdNoExistError('No such song', command='playid')
@handle_request(r'^play (?P<songpos>-?\d+)$') @handle_request(r'^play (?P<songpos>-?\d+)$')
@ -187,7 +189,7 @@ def playpos(context, songpos):
songpos, songpos + 1).get()[0] songpos, songpos + 1).get()[0]
return context.core.playback.play(cp_track).get() return context.core.playback.play(cp_track).get()
except IndexError: except IndexError:
raise MpdArgError(u'Bad song index', command=u'play') raise MpdArgError('Bad song index', command='play')
def _play_minus_one(context): def _play_minus_one(context):
@ -311,7 +313,7 @@ def replay_gain_status(context):
Prints replay gain options. Currently, only the variable Prints replay gain options. Currently, only the variable
``replay_gain_mode`` is returned. ``replay_gain_mode`` is returned.
""" """
return u'off' # TODO return 'off' # TODO
@handle_request(r'^seek (?P<songpos>\d+) (?P<seconds>\d+)$') @handle_request(r'^seek (?P<songpos>\d+) (?P<seconds>\d+)$')

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy.frontends.mpd.protocol import handle_request, mpd_commands from mopidy.frontends.mpd.protocol import handle_request, mpd_commands
from mopidy.frontends.mpd.exceptions import MpdNotImplemented from mopidy.frontends.mpd.exceptions import MpdNotImplemented
@ -93,5 +95,5 @@ def urlhandlers(context):
Gets a list of available URL handlers. Gets a list of available URL handlers.
""" """
return [ return [
(u'handler', uri_scheme) ('handler', uri_scheme)
for uri_scheme in context.core.uri_schemes.get()] for uri_scheme in context.core.uri_schemes.get()]

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import pykka import pykka
from mopidy.core import PlaybackState from mopidy.core import PlaybackState
@ -94,7 +96,7 @@ def idle(context, subsystems=None):
context.subscriptions = set() context.subscriptions = set()
for subsystem in active: for subsystem in active:
response.append(u'changed: %s' % subsystem) response.append('changed: %s' % subsystem)
return response return response
@ -257,21 +259,21 @@ def _status_songpos(futures):
def _status_state(futures): def _status_state(futures):
state = futures['playback.state'].get() state = futures['playback.state'].get()
if state == PlaybackState.PLAYING: if state == PlaybackState.PLAYING:
return u'play' return 'play'
elif state == PlaybackState.STOPPED: elif state == PlaybackState.STOPPED:
return u'stop' return 'stop'
elif state == PlaybackState.PAUSED: elif state == PlaybackState.PAUSED:
return u'pause' return 'pause'
def _status_time(futures): def _status_time(futures):
return u'%d:%d' % ( return '%d:%d' % (
futures['playback.time_position'].get() // 1000, futures['playback.time_position'].get() // 1000,
_status_time_total(futures) // 1000) _status_time_total(futures) // 1000)
def _status_time_elapsed(futures): def _status_time_elapsed(futures):
return u'%.3f' % (futures['playback.time_position'].get() / 1000.0) return '%.3f' % (futures['playback.time_position'].get() / 1000.0)
def _status_time_total(futures): def _status_time_total(futures):

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy.frontends.mpd.protocol import handle_request from mopidy.frontends.mpd.protocol import handle_request
from mopidy.frontends.mpd.exceptions import MpdNotImplemented from mopidy.frontends.mpd.exceptions import MpdNotImplemented

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import datetime as dt import datetime as dt
from mopidy.frontends.mpd.exceptions import MpdNoExistError, MpdNotImplemented from mopidy.frontends.mpd.exceptions import MpdNoExistError, MpdNotImplemented
@ -25,7 +27,7 @@ def listplaylist(context, name):
playlist = context.core.stored_playlists.get(name=name).get() playlist = context.core.stored_playlists.get(name=name).get()
return ['file: %s' % t.uri for t in playlist.tracks] return ['file: %s' % t.uri for t in playlist.tracks]
except LookupError: except LookupError:
raise MpdNoExistError(u'No such playlist', command=u'listplaylist') raise MpdNoExistError('No such playlist', command='listplaylist')
@handle_request(r'^listplaylistinfo (?P<name>\S+)$') @handle_request(r'^listplaylistinfo (?P<name>\S+)$')
@ -47,8 +49,7 @@ def listplaylistinfo(context, name):
playlist = context.core.stored_playlists.get(name=name).get() playlist = context.core.stored_playlists.get(name=name).get()
return playlist_to_mpd_format(playlist) return playlist_to_mpd_format(playlist)
except LookupError: except LookupError:
raise MpdNoExistError( raise MpdNoExistError('No such playlist', command='listplaylistinfo')
u'No such playlist', command=u'listplaylistinfo')
@handle_request(r'^listplaylists$') @handle_request(r'^listplaylists$')
@ -74,7 +75,7 @@ def listplaylists(context):
""" """
result = [] result = []
for playlist in context.core.stored_playlists.playlists.get(): for playlist in context.core.stored_playlists.playlists.get():
result.append((u'playlist', playlist.name)) result.append(('playlist', playlist.name))
last_modified = ( last_modified = (
playlist.last_modified or dt.datetime.now()).isoformat() playlist.last_modified or dt.datetime.now()).isoformat()
# Remove microseconds # Remove microseconds
@ -82,7 +83,7 @@ def listplaylists(context):
# Add time zone information # Add time zone information
# TODO Convert to UTC before adding Z # TODO Convert to UTC before adding Z
last_modified = last_modified + 'Z' last_modified = last_modified + 'Z'
result.append((u'Last-Modified', last_modified)) result.append(('Last-Modified', last_modified))
return result return result
@ -103,7 +104,7 @@ def load(context, name):
playlist = context.core.stored_playlists.get(name=name).get() playlist = context.core.stored_playlists.get(name=name).get()
context.core.current_playlist.append(playlist.tracks) context.core.current_playlist.append(playlist.tracks)
except LookupError: except LookupError:
raise MpdNoExistError(u'No such playlist', command=u'load') raise MpdNoExistError('No such playlist', command='load')
@handle_request(r'^playlistadd "(?P<name>[^"]+)" "(?P<uri>[^"]+)"$') @handle_request(r'^playlistadd "(?P<name>[^"]+)" "(?P<uri>[^"]+)"$')

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
from mopidy.frontends.mpd import dispatcher, protocol from mopidy.frontends.mpd import dispatcher, protocol
@ -21,18 +23,18 @@ class MpdSession(network.LineProtocol):
self.dispatcher = dispatcher.MpdDispatcher(session=self, core=core) self.dispatcher = dispatcher.MpdDispatcher(session=self, core=core)
def on_start(self): def on_start(self):
logger.info(u'New MPD connection from [%s]:%s', self.host, self.port) logger.info('New MPD connection from [%s]:%s', self.host, self.port)
self.send_lines([u'OK MPD %s' % protocol.VERSION]) self.send_lines(['OK MPD %s' % protocol.VERSION])
def on_line_received(self, line): def on_line_received(self, line):
logger.debug(u'Request from [%s]:%s: %s', self.host, self.port, line) logger.debug('Request from [%s]:%s: %s', self.host, self.port, line)
response = self.dispatcher.handle_request(line) response = self.dispatcher.handle_request(line)
if not response: if not response:
return return
logger.debug( logger.debug(
u'Response to [%s]:%s: %s', self.host, self.port, 'Response to [%s]:%s: %s', self.host, self.port,
formatting.indent(self.terminator.join(response))) formatting.indent(self.terminator.join(response)))
self.send_lines(response) self.send_lines(response)
@ -45,8 +47,8 @@ class MpdSession(network.LineProtocol):
return super(MpdSession, self).decode(line.decode('string_escape')) return super(MpdSession, self).decode(line.decode('string_escape'))
except ValueError: except ValueError:
logger.warning( logger.warning(
u'Stopping actor due to unescaping error, data ' 'Stopping actor due to unescaping error, data '
u'supplied by client was not valid.') 'supplied by client was not valid.')
self.stop() self.stop()
def close(self): def close(self):

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import os import os
import re import re
@ -93,7 +95,7 @@ def artists_to_mpd_format(artists):
""" """
artists = list(artists) artists = list(artists)
artists.sort(key=lambda a: a.name) artists.sort(key=lambda a: a.name)
return u', '.join([a.name for a in artists if a.name]) return ', '.join([a.name for a in artists if a.name])
def tracks_to_mpd_format(tracks, start=0, end=None): def tracks_to_mpd_format(tracks, start=0, end=None):
@ -178,7 +180,7 @@ def _add_to_tag_cache(result, folders, files):
def tracks_to_directory_tree(tracks): def tracks_to_directory_tree(tracks):
directories = ({}, []) directories = ({}, [])
for track in tracks: for track in tracks:
path = u'' path = ''
current = directories current = directories
local_folder = settings.LOCAL_MUSIC_PATH local_folder = settings.LOCAL_MUSIC_PATH

View File

@ -50,5 +50,7 @@ Now you can control Mopidy through the player object. Examples:
player.Quit(dbus_interface='org.mpris.MediaPlayer2') player.Quit(dbus_interface='org.mpris.MediaPlayer2')
""" """
from __future__ import unicode_literals
# flake8: noqa # flake8: noqa
from .actor import MprisFrontend from .actor import MprisFrontend

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
import pykka import pykka
@ -12,7 +14,7 @@ try:
import indicate import indicate
except ImportError as import_error: except ImportError as import_error:
indicate = None # noqa indicate = None # noqa
logger.debug(u'Startup notification will not be sent (%s)', import_error) logger.debug('Startup notification will not be sent (%s)', import_error)
class MprisFrontend(pykka.ThreadingActor, CoreListener): class MprisFrontend(pykka.ThreadingActor, CoreListener):
@ -27,20 +29,20 @@ class MprisFrontend(pykka.ThreadingActor, CoreListener):
self.mpris_object = objects.MprisObject(self.core) self.mpris_object = objects.MprisObject(self.core)
self._send_startup_notification() self._send_startup_notification()
except Exception as e: except Exception as e:
logger.error(u'MPRIS frontend setup failed (%s)', e) logger.error('MPRIS frontend setup failed (%s)', e)
self.stop() self.stop()
def on_stop(self): def on_stop(self):
logger.debug(u'Removing MPRIS object from D-Bus connection...') logger.debug('Removing MPRIS object from D-Bus connection...')
if self.mpris_object: if self.mpris_object:
self.mpris_object.remove_from_connection() self.mpris_object.remove_from_connection()
self.mpris_object = None self.mpris_object = None
logger.debug(u'Removed MPRIS object from D-Bus connection') logger.debug('Removed MPRIS object from D-Bus connection')
def _send_startup_notification(self): def _send_startup_notification(self):
""" """
Send startup notification using libindicate to make Mopidy appear in Send startup notification using libindicate to make Mopidy appear in
e.g. `Ubuntu's sound menu <https://wiki.ubuntu.com/SoundMenu>`_. e.g. `Ubunt's sound menu <https://wiki.ubuntu.com/SoundMenu>`_.
A reference to the libindicate server is kept for as long as Mopidy is A reference to the libindicate server is kept for as long as Mopidy is
running. When Mopidy exits, the server will be unreferenced and Mopidy running. When Mopidy exits, the server will be unreferenced and Mopidy
@ -48,12 +50,12 @@ class MprisFrontend(pykka.ThreadingActor, CoreListener):
""" """
if not indicate: if not indicate:
return return
logger.debug(u'Sending startup notification...') logger.debug('Sending startup notification...')
self.indicate_server = indicate.Server() self.indicate_server = indicate.Server()
self.indicate_server.set_type('music.mopidy') self.indicate_server.set_type('music.mopidy')
self.indicate_server.set_desktop_file(settings.DESKTOP_FILE) self.indicate_server.set_desktop_file(settings.DESKTOP_FILE)
self.indicate_server.show() self.indicate_server.show()
logger.debug(u'Startup notification sent') logger.debug('Startup notification sent')
def _emit_properties_changed(self, *changed_properties): def _emit_properties_changed(self, *changed_properties):
if self.mpris_object is None: if self.mpris_object is None:
@ -65,25 +67,25 @@ class MprisFrontend(pykka.ThreadingActor, CoreListener):
objects.PLAYER_IFACE, dict(props_with_new_values), []) objects.PLAYER_IFACE, dict(props_with_new_values), [])
def track_playback_paused(self, track, time_position): def track_playback_paused(self, track, time_position):
logger.debug(u'Received track playback paused event') logger.debug('Received track playback paused event')
self._emit_properties_changed('PlaybackStatus') self._emit_properties_changed('PlaybackStatus')
def track_playback_resumed(self, track, time_position): def track_playback_resumed(self, track, time_position):
logger.debug(u'Received track playback resumed event') logger.debug('Received track playback resumed event')
self._emit_properties_changed('PlaybackStatus') self._emit_properties_changed('PlaybackStatus')
def track_playback_started(self, track): def track_playback_started(self, track):
logger.debug(u'Received track playback started event') logger.debug('Received track playback started event')
self._emit_properties_changed('PlaybackStatus', 'Metadata') self._emit_properties_changed('PlaybackStatus', 'Metadata')
def track_playback_ended(self, track, time_position): def track_playback_ended(self, track, time_position):
logger.debug(u'Received track playback ended event') logger.debug('Received track playback ended event')
self._emit_properties_changed('PlaybackStatus', 'Metadata') self._emit_properties_changed('PlaybackStatus', 'Metadata')
def volume_changed(self): def volume_changed(self):
logger.debug(u'Received volume changed event') logger.debug('Received volume changed event')
self._emit_properties_changed('Volume') self._emit_properties_changed('Volume')
def seeked(self, time_position_in_ms): def seeked(self, time_position_in_ms):
logger.debug(u'Received seeked event') logger.debug('Received seeked event')
self.mpris_object.Seeked(time_position_in_ms * 1000) self.mpris_object.Seeked(time_position_in_ms * 1000)

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
import os import os
@ -75,11 +77,11 @@ class MprisObject(dbus.service.Object):
} }
def _connect_to_dbus(self): def _connect_to_dbus(self):
logger.debug(u'Connecting to D-Bus...') logger.debug('Connecting to D-Bus...')
mainloop = dbus.mainloop.glib.DBusGMainLoop() mainloop = dbus.mainloop.glib.DBusGMainLoop()
bus_name = dbus.service.BusName( bus_name = dbus.service.BusName(
BUS_NAME, dbus.SessionBus(mainloop=mainloop)) BUS_NAME, dbus.SessionBus(mainloop=mainloop))
logger.info(u'Connected to D-Bus') logger.info('Connected to D-Bus')
return bus_name return bus_name
def _get_track_id(self, cp_track): def _get_track_id(self, cp_track):
@ -95,7 +97,7 @@ class MprisObject(dbus.service.Object):
in_signature='ss', out_signature='v') in_signature='ss', out_signature='v')
def Get(self, interface, prop): def Get(self, interface, prop):
logger.debug( logger.debug(
u'%s.Get(%s, %s) called', '%s.Get(%s, %s) called',
dbus.PROPERTIES_IFACE, repr(interface), repr(prop)) dbus.PROPERTIES_IFACE, repr(interface), repr(prop))
(getter, _) = self.properties[interface][prop] (getter, _) = self.properties[interface][prop]
if callable(getter): if callable(getter):
@ -107,7 +109,7 @@ class MprisObject(dbus.service.Object):
in_signature='s', out_signature='a{sv}') in_signature='s', out_signature='a{sv}')
def GetAll(self, interface): def GetAll(self, interface):
logger.debug( logger.debug(
u'%s.GetAll(%s) called', dbus.PROPERTIES_IFACE, repr(interface)) '%s.GetAll(%s) called', dbus.PROPERTIES_IFACE, repr(interface))
getters = {} getters = {}
for key, (getter, _) in self.properties[interface].iteritems(): for key, (getter, _) in self.properties[interface].iteritems():
getters[key] = getter() if callable(getter) else getter getters[key] = getter() if callable(getter) else getter
@ -117,7 +119,7 @@ class MprisObject(dbus.service.Object):
in_signature='ssv', out_signature='') in_signature='ssv', out_signature='')
def Set(self, interface, prop, value): def Set(self, interface, prop, value):
logger.debug( logger.debug(
u'%s.Set(%s, %s, %s) called', '%s.Set(%s, %s, %s) called',
dbus.PROPERTIES_IFACE, repr(interface), repr(prop), repr(value)) dbus.PROPERTIES_IFACE, repr(interface), repr(prop), repr(value))
_, setter = self.properties[interface][prop] _, setter = self.properties[interface][prop]
if setter is not None: if setter is not None:
@ -130,7 +132,7 @@ class MprisObject(dbus.service.Object):
def PropertiesChanged(self, interface, changed_properties, def PropertiesChanged(self, interface, changed_properties,
invalidated_properties): invalidated_properties):
logger.debug( logger.debug(
u'%s.PropertiesChanged(%s, %s, %s) signaled', '%s.PropertiesChanged(%s, %s, %s) signaled',
dbus.PROPERTIES_IFACE, interface, changed_properties, dbus.PROPERTIES_IFACE, interface, changed_properties,
invalidated_properties) invalidated_properties)
@ -138,12 +140,12 @@ class MprisObject(dbus.service.Object):
@dbus.service.method(dbus_interface=ROOT_IFACE) @dbus.service.method(dbus_interface=ROOT_IFACE)
def Raise(self): def Raise(self):
logger.debug(u'%s.Raise called', ROOT_IFACE) logger.debug('%s.Raise called', ROOT_IFACE)
# Do nothing, as we do not have a GUI # Do nothing, as we do not have a GUI
@dbus.service.method(dbus_interface=ROOT_IFACE) @dbus.service.method(dbus_interface=ROOT_IFACE)
def Quit(self): def Quit(self):
logger.debug(u'%s.Quit called', ROOT_IFACE) logger.debug('%s.Quit called', ROOT_IFACE)
exit_process() exit_process()
### Root interface properties ### Root interface properties
@ -158,33 +160,33 @@ class MprisObject(dbus.service.Object):
@dbus.service.method(dbus_interface=PLAYER_IFACE) @dbus.service.method(dbus_interface=PLAYER_IFACE)
def Next(self): def Next(self):
logger.debug(u'%s.Next called', PLAYER_IFACE) logger.debug('%s.Next called', PLAYER_IFACE)
if not self.get_CanGoNext(): if not self.get_CanGoNext():
logger.debug(u'%s.Next not allowed', PLAYER_IFACE) logger.debug('%s.Next not allowed', PLAYER_IFACE)
return return
self.core.playback.next().get() self.core.playback.next().get()
@dbus.service.method(dbus_interface=PLAYER_IFACE) @dbus.service.method(dbus_interface=PLAYER_IFACE)
def Previous(self): def Previous(self):
logger.debug(u'%s.Previous called', PLAYER_IFACE) logger.debug('%s.Previous called', PLAYER_IFACE)
if not self.get_CanGoPrevious(): if not self.get_CanGoPrevious():
logger.debug(u'%s.Previous not allowed', PLAYER_IFACE) logger.debug('%s.Previous not allowed', PLAYER_IFACE)
return return
self.core.playback.previous().get() self.core.playback.previous().get()
@dbus.service.method(dbus_interface=PLAYER_IFACE) @dbus.service.method(dbus_interface=PLAYER_IFACE)
def Pause(self): def Pause(self):
logger.debug(u'%s.Pause called', PLAYER_IFACE) logger.debug('%s.Pause called', PLAYER_IFACE)
if not self.get_CanPause(): if not self.get_CanPause():
logger.debug(u'%s.Pause not allowed', PLAYER_IFACE) logger.debug('%s.Pause not allowed', PLAYER_IFACE)
return return
self.core.playback.pause().get() self.core.playback.pause().get()
@dbus.service.method(dbus_interface=PLAYER_IFACE) @dbus.service.method(dbus_interface=PLAYER_IFACE)
def PlayPause(self): def PlayPause(self):
logger.debug(u'%s.PlayPause called', PLAYER_IFACE) logger.debug('%s.PlayPause called', PLAYER_IFACE)
if not self.get_CanPause(): if not self.get_CanPause():
logger.debug(u'%s.PlayPause not allowed', PLAYER_IFACE) logger.debug('%s.PlayPause not allowed', PLAYER_IFACE)
return return
state = self.core.playback.state.get() state = self.core.playback.state.get()
if state == PlaybackState.PLAYING: if state == PlaybackState.PLAYING:
@ -196,17 +198,17 @@ class MprisObject(dbus.service.Object):
@dbus.service.method(dbus_interface=PLAYER_IFACE) @dbus.service.method(dbus_interface=PLAYER_IFACE)
def Stop(self): def Stop(self):
logger.debug(u'%s.Stop called', PLAYER_IFACE) logger.debug('%s.Stop called', PLAYER_IFACE)
if not self.get_CanControl(): if not self.get_CanControl():
logger.debug(u'%s.Stop not allowed', PLAYER_IFACE) logger.debug('%s.Stop not allowed', PLAYER_IFACE)
return return
self.core.playback.stop().get() self.core.playback.stop().get()
@dbus.service.method(dbus_interface=PLAYER_IFACE) @dbus.service.method(dbus_interface=PLAYER_IFACE)
def Play(self): def Play(self):
logger.debug(u'%s.Play called', PLAYER_IFACE) logger.debug('%s.Play called', PLAYER_IFACE)
if not self.get_CanPlay(): if not self.get_CanPlay():
logger.debug(u'%s.Play not allowed', PLAYER_IFACE) logger.debug('%s.Play not allowed', PLAYER_IFACE)
return return
state = self.core.playback.state.get() state = self.core.playback.state.get()
if state == PlaybackState.PAUSED: if state == PlaybackState.PAUSED:
@ -216,9 +218,9 @@ class MprisObject(dbus.service.Object):
@dbus.service.method(dbus_interface=PLAYER_IFACE) @dbus.service.method(dbus_interface=PLAYER_IFACE)
def Seek(self, offset): def Seek(self, offset):
logger.debug(u'%s.Seek called', PLAYER_IFACE) logger.debug('%s.Seek called', PLAYER_IFACE)
if not self.get_CanSeek(): if not self.get_CanSeek():
logger.debug(u'%s.Seek not allowed', PLAYER_IFACE) logger.debug('%s.Seek not allowed', PLAYER_IFACE)
return return
offset_in_milliseconds = offset // 1000 offset_in_milliseconds = offset // 1000
current_position = self.core.playback.time_position.get() current_position = self.core.playback.time_position.get()
@ -227,9 +229,9 @@ class MprisObject(dbus.service.Object):
@dbus.service.method(dbus_interface=PLAYER_IFACE) @dbus.service.method(dbus_interface=PLAYER_IFACE)
def SetPosition(self, track_id, position): def SetPosition(self, track_id, position):
logger.debug(u'%s.SetPosition called', PLAYER_IFACE) logger.debug('%s.SetPosition called', PLAYER_IFACE)
if not self.get_CanSeek(): if not self.get_CanSeek():
logger.debug(u'%s.SetPosition not allowed', PLAYER_IFACE) logger.debug('%s.SetPosition not allowed', PLAYER_IFACE)
return return
position = position // 1000 position = position // 1000
current_cp_track = self.core.playback.current_cp_track.get() current_cp_track = self.core.playback.current_cp_track.get()
@ -245,11 +247,11 @@ class MprisObject(dbus.service.Object):
@dbus.service.method(dbus_interface=PLAYER_IFACE) @dbus.service.method(dbus_interface=PLAYER_IFACE)
def OpenUri(self, uri): def OpenUri(self, uri):
logger.debug(u'%s.OpenUri called', PLAYER_IFACE) logger.debug('%s.OpenUri called', PLAYER_IFACE)
if not self.get_CanPlay(): if not self.get_CanPlay():
# NOTE The spec does not explictly require this check, but guarding # NOTE The spec does not explictly require this check, but guarding
# the other methods doesn't help much if OpenUri is open for use. # the other methods doesn't help much if OpenUri is open for use.
logger.debug(u'%s.Play not allowed', PLAYER_IFACE) logger.debug('%s.Play not allowed', PLAYER_IFACE)
return return
# NOTE Check if URI has MIME type known to the backend, if MIME support # NOTE Check if URI has MIME type known to the backend, if MIME support
# is added to the backend. # is added to the backend.
@ -261,13 +263,13 @@ class MprisObject(dbus.service.Object):
cp_track = self.core.current_playlist.add(track).get() cp_track = self.core.current_playlist.add(track).get()
self.core.playback.play(cp_track) self.core.playback.play(cp_track)
else: else:
logger.debug(u'Track with URI "%s" not found in library.', uri) logger.debug('Track with URI "%s" not found in library.', uri)
### Player interface signals ### Player interface signals
@dbus.service.signal(dbus_interface=PLAYER_IFACE, signature='x') @dbus.service.signal(dbus_interface=PLAYER_IFACE, signature='x')
def Seeked(self, position): def Seeked(self, position):
logger.debug(u'%s.Seeked signaled', PLAYER_IFACE) logger.debug('%s.Seeked signaled', PLAYER_IFACE)
# Do nothing, as just calling the method is enough to emit the signal. # Do nothing, as just calling the method is enough to emit the signal.
### Player interface properties ### Player interface properties
@ -294,7 +296,7 @@ class MprisObject(dbus.service.Object):
def set_LoopStatus(self, value): def set_LoopStatus(self, value):
if not self.get_CanControl(): if not self.get_CanControl():
logger.debug(u'Setting %s.LoopStatus not allowed', PLAYER_IFACE) logger.debug('Setting %s.LoopStatus not allowed', PLAYER_IFACE)
return return
if value == 'None': if value == 'None':
self.core.playback.repeat = False self.core.playback.repeat = False
@ -310,7 +312,7 @@ class MprisObject(dbus.service.Object):
if not self.get_CanControl(): if not self.get_CanControl():
# NOTE The spec does not explictly require this check, but it was # NOTE The spec does not explictly require this check, but it was
# added to be consistent with all the other property setters. # added to be consistent with all the other property setters.
logger.debug(u'Setting %s.Rate not allowed', PLAYER_IFACE) logger.debug('Setting %s.Rate not allowed', PLAYER_IFACE)
return return
if value == 0: if value == 0:
self.Pause() self.Pause()
@ -320,7 +322,7 @@ class MprisObject(dbus.service.Object):
def set_Shuffle(self, value): def set_Shuffle(self, value):
if not self.get_CanControl(): if not self.get_CanControl():
logger.debug(u'Setting %s.Shuffle not allowed', PLAYER_IFACE) logger.debug('Setting %s.Shuffle not allowed', PLAYER_IFACE)
return return
if value: if value:
self.core.playback.random = True self.core.playback.random = True
@ -364,7 +366,7 @@ class MprisObject(dbus.service.Object):
def set_Volume(self, value): def set_Volume(self, value):
if not self.get_CanControl(): if not self.get_CanControl():
logger.debug(u'Setting %s.Volume not allowed', PLAYER_IFACE) logger.debug('Setting %s.Volume not allowed', PLAYER_IFACE)
return return
if value is None: if value is None:
return return

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from collections import namedtuple from collections import namedtuple
@ -14,7 +16,7 @@ class ImmutableObject(object):
for key, value in kwargs.items(): for key, value in kwargs.items():
if not hasattr(self, key): if not hasattr(self, key):
raise TypeError( raise TypeError(
u"__init__() got an unexpected keyword argument '%s'" % '__init__() got an unexpected keyword argument "%s"' %
key) key)
self.__dict__[key] = value self.__dict__[key] = value
@ -73,7 +75,7 @@ class ImmutableObject(object):
data[key] = values.pop(key) data[key] = values.pop(key)
if values: if values:
raise TypeError( raise TypeError(
u"copy() got an unexpected keyword argument '%s'" % key) 'copy() got an unexpected keyword argument "%s"' % key)
return self.__class__(**data) return self.__class__(**data)
def serialize(self): def serialize(self):

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import gobject import gobject
gobject.threads_init() gobject.threads_init()
@ -62,7 +64,7 @@ class Scanner(object):
fakesink = gst.element_factory_make('fakesink') fakesink = gst.element_factory_make('fakesink')
self.uribin = gst.element_factory_make('uridecodebin') self.uribin = gst.element_factory_make('uridecodebin')
self.uribin.set_property('caps', gst.Caps('audio/x-raw-int')) self.uribin.set_property('caps', gst.Caps(b'audio/x-raw-int'))
self.uribin.connect( self.uribin.connect(
'pad-added', self.process_new_pad, fakesink.get_pad('sink')) 'pad-added', self.process_new_pad, fakesink.get_pad('sink'))

View File

@ -7,6 +7,8 @@ All available settings and their default values.
file called ``~/.config/mopidy/settings.py`` and redefine settings there. file called ``~/.config/mopidy/settings.py`` and redefine settings there.
""" """
from __future__ import unicode_literals
#: List of playback backends to use. See :ref:`backend-implementations` for all #: List of playback backends to use. See :ref:`backend-implementations` for all
#: available backends. #: available backends.
#: #:
@ -20,21 +22,21 @@ All available settings and their default values.
#: u'mopidy.backends.spotify.SpotifyBackend', #: u'mopidy.backends.spotify.SpotifyBackend',
#: ) #: )
BACKENDS = ( BACKENDS = (
u'mopidy.backends.local.LocalBackend', 'mopidy.backends.local.LocalBackend',
u'mopidy.backends.spotify.SpotifyBackend', 'mopidy.backends.spotify.SpotifyBackend',
) )
#: The log format used for informational logging. #: The log format used for informational logging.
#: #:
#: See http://docs.python.org/2/library/logging.html#formatter-objects for #: See http://docs.python.org/2/library/logging.html#formatter-objects for
#: details on the format. #: details on the format.
CONSOLE_LOG_FORMAT = u'%(levelname)-8s %(message)s' CONSOLE_LOG_FORMAT = '%(levelname)-8s %(message)s'
#: The log format used for debug logging. #: The log format used for debug logging.
#: #:
#: See http://docs.python.org/library/logging.html#formatter-objects for #: See http://docs.python.org/library/logging.html#formatter-objects for
#: details on the format. #: details on the format.
DEBUG_LOG_FORMAT = u'%(levelname)-8s %(asctime)s' + \ DEBUG_LOG_FORMAT = '%(levelname)-8s %(asctime)s' + \
' [%(process)d:%(threadName)s] %(name)s\n %(message)s' ' [%(process)d:%(threadName)s] %(name)s\n %(message)s'
#: The file to dump debug log data to when Mopidy is run with the #: The file to dump debug log data to when Mopidy is run with the
@ -43,13 +45,13 @@ DEBUG_LOG_FORMAT = u'%(levelname)-8s %(asctime)s' + \
#: Default:: #: Default::
#: #:
#: DEBUG_LOG_FILENAME = u'mopidy.log' #: DEBUG_LOG_FILENAME = u'mopidy.log'
DEBUG_LOG_FILENAME = u'mopidy.log' DEBUG_LOG_FILENAME = 'mopidy.log'
#: If we should start a background thread that dumps thread's traceback when we #: If we should start a background thread that dumps thread's traceback when we
#: get a SIGUSR1. Mainly a debug tool for figuring out deadlocks. #: get a SIGUSR1. Mainly a debug tool for figuring out deadlocks.
#: #:
#: Default:: #: Default::
#: #:
#: DEBUG_THREAD = False #: DEBUG_THREAD = False
DEBUG_THREAD = False DEBUG_THREAD = False
@ -60,7 +62,7 @@ DEBUG_THREAD = False
#: Default:: #: Default::
#: #:
#: DESKTOP_FILE = u'/usr/share/applications/mopidy.desktop' #: DESKTOP_FILE = u'/usr/share/applications/mopidy.desktop'
DESKTOP_FILE = u'/usr/share/applications/mopidy.desktop' DESKTOP_FILE = '/usr/share/applications/mopidy.desktop'
#: List of server frontends to use. See :ref:`frontend-implementations` for #: List of server frontends to use. See :ref:`frontend-implementations` for
#: available frontends. #: available frontends.
@ -73,20 +75,20 @@ DESKTOP_FILE = u'/usr/share/applications/mopidy.desktop'
#: u'mopidy.frontends.mpris.MprisFrontend', #: u'mopidy.frontends.mpris.MprisFrontend',
#: ) #: )
FRONTENDS = ( FRONTENDS = (
u'mopidy.frontends.mpd.MpdFrontend', 'mopidy.frontends.mpd.MpdFrontend',
u'mopidy.frontends.lastfm.LastfmFrontend', 'mopidy.frontends.lastfm.LastfmFrontend',
u'mopidy.frontends.mpris.MprisFrontend', 'mopidy.frontends.mpris.MprisFrontend',
) )
#: Your `Last.fm <http://www.last.fm/>`_ username. #: Your `Last.fm <http://www.last.fm/>`_ username.
#: #:
#: Used by :mod:`mopidy.frontends.lastfm`. #: Used by :mod:`mopidy.frontends.lastfm`.
LASTFM_USERNAME = u'' LASTFM_USERNAME = ''
#: Your `Last.fm <http://www.last.fm/>`_ password. #: Your `Last.fm <http://www.last.fm/>`_ password.
#: #:
#: Used by :mod:`mopidy.frontends.lastfm`. #: Used by :mod:`mopidy.frontends.lastfm`.
LASTFM_PASSWORD = u'' LASTFM_PASSWORD = ''
#: Path to folder with local music. #: Path to folder with local music.
#: #:
@ -95,7 +97,7 @@ LASTFM_PASSWORD = u''
#: Default:: #: Default::
#: #:
#: LOCAL_MUSIC_PATH = u'$XDG_MUSIC_DIR' #: LOCAL_MUSIC_PATH = u'$XDG_MUSIC_DIR'
LOCAL_MUSIC_PATH = u'$XDG_MUSIC_DIR' LOCAL_MUSIC_PATH = '$XDG_MUSIC_DIR'
#: Path to playlist folder with m3u files for local music. #: Path to playlist folder with m3u files for local music.
#: #:
@ -104,7 +106,7 @@ LOCAL_MUSIC_PATH = u'$XDG_MUSIC_DIR'
#: Default:: #: Default::
#: #:
#: LOCAL_PLAYLIST_PATH = u'$XDG_DATA_DIR/mopidy/playlists' #: LOCAL_PLAYLIST_PATH = u'$XDG_DATA_DIR/mopidy/playlists'
LOCAL_PLAYLIST_PATH = u'$XDG_DATA_DIR/mopidy/playlists' LOCAL_PLAYLIST_PATH = '$XDG_DATA_DIR/mopidy/playlists'
#: Path to tag cache for local music. #: Path to tag cache for local music.
#: #:
@ -113,7 +115,7 @@ LOCAL_PLAYLIST_PATH = u'$XDG_DATA_DIR/mopidy/playlists'
#: Default:: #: Default::
#: #:
#: LOCAL_TAG_CACHE_FILE = u'$XDG_DATA_DIR/mopidy/tag_cache' #: LOCAL_TAG_CACHE_FILE = u'$XDG_DATA_DIR/mopidy/tag_cache'
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. #: Audio mixer to use.
#: #:
@ -126,7 +128,7 @@ LOCAL_TAG_CACHE_FILE = u'$XDG_DATA_DIR/mopidy/tag_cache'
#: Default:: #: Default::
#: #:
#: MIXER = u'autoaudiomixer' #: MIXER = u'autoaudiomixer'
MIXER = u'autoaudiomixer' MIXER = 'autoaudiomixer'
#: Audio mixer track to use. #: Audio mixer track to use.
#: #:
@ -153,7 +155,7 @@ MIXER_TRACK = None
#: Listens on all IPv4 interfaces. #: Listens on all IPv4 interfaces.
#: ``::`` #: ``::``
#: Listens on all interfaces, both IPv4 and IPv6. #: Listens on all interfaces, both IPv4 and IPv6.
MPD_SERVER_HOSTNAME = u'127.0.0.1' MPD_SERVER_HOSTNAME = '127.0.0.1'
#: Which TCP port Mopidy's MPD server should listen to. #: Which TCP port Mopidy's MPD server should listen to.
#: #:
@ -185,7 +187,7 @@ MPD_SERVER_MAX_CONNECTIONS = 20
#: Default:: #: Default::
#: #:
#: OUTPUT = u'autoaudiosink' #: OUTPUT = u'autoaudiosink'
OUTPUT = u'autoaudiosink' OUTPUT = 'autoaudiosink'
#: Path to the Spotify cache. #: Path to the Spotify cache.
#: #:
@ -194,17 +196,17 @@ OUTPUT = u'autoaudiosink'
#: Default:: #: Default::
#: #:
#: SPOTIFY_CACHE_PATH = u'$XDG_CACHE_DIR/mopidy/spotify' #: SPOTIFY_CACHE_PATH = u'$XDG_CACHE_DIR/mopidy/spotify'
SPOTIFY_CACHE_PATH = u'$XDG_CACHE_DIR/mopidy/spotify' SPOTIFY_CACHE_PATH = '$XDG_CACHE_DIR/mopidy/spotify'
#: Your Spotify Premium username. #: Your Spotify Premium username.
#: #:
#: Used by :mod:`mopidy.backends.spotify`. #: Used by :mod:`mopidy.backends.spotify`.
SPOTIFY_USERNAME = u'' SPOTIFY_USERNAME = ''
#: Your Spotify Premium password. #: Your Spotify Premium password.
#: #:
#: Used by :mod:`mopidy.backends.spotify`. #: Used by :mod:`mopidy.backends.spotify`.
SPOTIFY_PASSWORD = u'' SPOTIFY_PASSWORD = ''
#: Spotify preferred bitrate. #: Spotify preferred bitrate.
#: #:

View File

@ -0,0 +1 @@
from __future__ import unicode_literals

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import os import os
import platform import platform
import sys import sys

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import locale import locale

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import re import re
import unicodedata import unicodedata
@ -6,7 +8,7 @@ def indent(string, places=4, linebreak='\n'):
lines = string.split(linebreak) lines = string.split(linebreak)
if len(lines) == 1: if len(lines) == 1:
return string return string
result = u'' result = ''
for line in lines: for line in lines:
result += linebreak + ' ' * places + line result += linebreak + ' ' * places + line
return result return result

View File

@ -1,6 +1,9 @@
from __future__ import unicode_literals
import logging import logging
import sys import sys
logger = logging.getLogger('mopidy.utils') logger = logging.getLogger('mopidy.utils')

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
import logging.handlers import logging.handlers
@ -14,9 +16,9 @@ def setup_logging(verbosity_level, save_debug_log):
# New in Python 2.7 # New in Python 2.7
logging.captureWarnings(True) logging.captureWarnings(True)
logger = logging.getLogger('mopidy.utils.log') logger = logging.getLogger('mopidy.utils.log')
logger.info(u'Starting Mopidy %s', versioning.get_version()) logger.info('Starting Mopidy %s', versioning.get_version())
logger.info(u'%(name)s: %(version)s', deps.platform_info()) logger.info('%(name)s: %(version)s', deps.platform_info())
logger.info(u'%(name)s: %(version)s', deps.python_info()) logger.info('%(name)s: %(version)s', deps.python_info())
def setup_root_logger(): def setup_root_logger():

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import errno import errno
import gobject import gobject
import logging import logging
@ -26,8 +28,8 @@ def try_ipv6_socket():
return True return True
except IOError as error: except IOError as error:
logger.debug( logger.debug(
u'Platform supports IPv6, but socket creation failed, ' 'Platform supports IPv6, but socket creation failed, '
u'disabling: %s', 'disabling: %s',
encoding.locale_decode(error)) encoding.locale_decode(error))
return False return False
@ -107,7 +109,7 @@ class Server(object):
def reject_connection(self, sock, addr): def reject_connection(self, sock, addr):
# FIXME provide more context in logging? # FIXME provide more context in logging?
logger.warning(u'Rejected connection from [%s]:%s', addr[0], addr[1]) logger.warning('Rejected connection from [%s]:%s', addr[0], addr[1])
try: try:
sock.close() sock.close()
except socket.error: except socket.error:
@ -190,7 +192,7 @@ class Connection(object):
except socket.error as e: except socket.error as e:
if e.errno in (errno.EWOULDBLOCK, errno.EINTR): if e.errno in (errno.EWOULDBLOCK, errno.EINTR):
return data return data
self.stop(u'Unexpected client error: %s' % e) self.stop('Unexpected client error: %s' % e)
return '' return ''
def enable_timeout(self): def enable_timeout(self):
@ -219,7 +221,7 @@ class Connection(object):
gobject.IO_IN | gobject.IO_ERR | gobject.IO_HUP, gobject.IO_IN | gobject.IO_ERR | gobject.IO_HUP,
self.recv_callback) self.recv_callback)
except socket.error as e: except socket.error as e:
self.stop(u'Problem with connection: %s' % e) self.stop('Problem with connection: %s' % e)
def disable_recv(self): def disable_recv(self):
if self.recv_id is None: if self.recv_id is None:
@ -237,7 +239,7 @@ class Connection(object):
gobject.IO_OUT | gobject.IO_ERR | gobject.IO_HUP, gobject.IO_OUT | gobject.IO_ERR | gobject.IO_HUP,
self.send_callback) self.send_callback)
except socket.error as e: except socket.error as e:
self.stop(u'Problem with connection: %s' % e) self.stop('Problem with connection: %s' % e)
def disable_send(self): def disable_send(self):
if self.send_id is None: if self.send_id is None:
@ -248,30 +250,30 @@ class Connection(object):
def recv_callback(self, fd, flags): def recv_callback(self, fd, flags):
if flags & (gobject.IO_ERR | gobject.IO_HUP): if flags & (gobject.IO_ERR | gobject.IO_HUP):
self.stop(u'Bad client flags: %s' % flags) self.stop('Bad client flags: %s' % flags)
return True return True
try: try:
data = self.sock.recv(4096) data = self.sock.recv(4096)
except socket.error as e: except socket.error as e:
if e.errno not in (errno.EWOULDBLOCK, errno.EINTR): if e.errno not in (errno.EWOULDBLOCK, errno.EINTR):
self.stop(u'Unexpected client error: %s' % e) self.stop('Unexpected client error: %s' % e)
return True return True
if not data: if not data:
self.stop(u'Client most likely disconnected.') self.stop('Client most likely disconnected.')
return True return True
try: try:
self.actor_ref.tell({'received': data}) self.actor_ref.tell({'received': data})
except pykka.ActorDeadError: except pykka.ActorDeadError:
self.stop(u'Actor is dead.') self.stop('Actor is dead.')
return True return True
def send_callback(self, fd, flags): def send_callback(self, fd, flags):
if flags & (gobject.IO_ERR | gobject.IO_HUP): if flags & (gobject.IO_ERR | gobject.IO_HUP):
self.stop(u'Bad client flags: %s' % flags) self.stop('Bad client flags: %s' % flags)
return True return True
# If with can't get the lock, simply try again next time socket is # If with can't get the lock, simply try again next time socket is
@ -289,7 +291,7 @@ class Connection(object):
return True return True
def timeout_callback(self): def timeout_callback(self):
self.stop(u'Client timeout out after %s seconds' % self.timeout) self.stop('Client timeout out after %s seconds' % self.timeout)
return False return False
@ -356,7 +358,7 @@ class LineProtocol(pykka.ThreadingActor):
def on_stop(self): def on_stop(self):
"""Ensure that cleanup when actor stops.""" """Ensure that cleanup when actor stops."""
self.connection.stop(u'Actor is shutting down.') self.connection.stop('Actor is shutting down.')
def parse_lines(self): def parse_lines(self):
"""Consume new data and yield any lines found.""" """Consume new data and yield any lines found."""
@ -375,8 +377,8 @@ class LineProtocol(pykka.ThreadingActor):
return line.encode(self.encoding) return line.encode(self.encoding)
except UnicodeError: except UnicodeError:
logger.warning( logger.warning(
u'Stopping actor due to encode problem, data ' 'Stopping actor due to encode problem, data '
u'supplied by client was not valid %s', 'supplied by client was not valid %s',
self.encoding) self.encoding)
self.stop() self.stop()
@ -390,14 +392,14 @@ class LineProtocol(pykka.ThreadingActor):
return line.decode(self.encoding) return line.decode(self.encoding)
except UnicodeError: except UnicodeError:
logger.warning( logger.warning(
u'Stopping actor due to decode problem, data ' 'Stopping actor due to decode problem, data '
u'supplied by client was not valid %s', 'supplied by client was not valid %s',
self.encoding) self.encoding)
self.stop() self.stop()
def join_lines(self, lines): def join_lines(self, lines):
if not lines: if not lines:
return u'' return ''
return self.terminator.join(lines) + self.terminator return self.terminator.join(lines) + self.terminator
def send_lines(self, lines): def send_lines(self, lines):

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
import os import os
import re import re
@ -25,10 +27,10 @@ def get_or_create_folder(folder):
folder = os.path.expanduser(folder) folder = os.path.expanduser(folder)
if os.path.isfile(folder): if os.path.isfile(folder):
raise OSError( raise OSError(
u'A file with the same name as the desired dir, ' 'A file with the same name as the desired dir, '
u'"%s", already exists.' % folder) '"%s", already exists.' % folder)
elif not os.path.isdir(folder): elif not os.path.isdir(folder):
logger.info(u'Creating dir %s', folder) logger.info('Creating dir %s', folder)
os.makedirs(folder, 0755) os.makedirs(folder, 0755)
return folder return folder
@ -36,7 +38,7 @@ def get_or_create_folder(folder):
def get_or_create_file(filename): def get_or_create_file(filename):
filename = os.path.expanduser(filename) filename = os.path.expanduser(filename)
if not os.path.isfile(filename): if not os.path.isfile(filename):
logger.info(u'Creating file %s', filename) logger.info('Creating file %s', filename)
open(filename, 'w') open(filename, 'w')
return filename return filename

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging import logging
import signal import signal
import sys import sys
@ -10,26 +12,29 @@ from pykka.registry import ActorRegistry
from mopidy import exceptions from mopidy import exceptions
logger = logging.getLogger('mopidy.utils.process') logger = logging.getLogger('mopidy.utils.process')
SIGNALS = dict((k, v) for v, k in signal.__dict__.iteritems() SIGNALS = dict((k, v) for v, k in signal.__dict__.iteritems()
if v.startswith('SIG') and not v.startswith('SIG_')) if v.startswith('SIG') and not v.startswith('SIG_'))
def exit_process(): def exit_process():
logger.debug(u'Interrupting main...') logger.debug('Interrupting main...')
thread.interrupt_main() thread.interrupt_main()
logger.debug(u'Interrupted main') logger.debug('Interrupted main')
def exit_handler(signum, frame): def exit_handler(signum, frame):
"""A :mod:`signal` handler which will exit the program on signal.""" """A :mod:`signal` handler which will exit the program on signal."""
logger.info(u'Got %s signal', SIGNALS[signum]) logger.info('Got %s signal', SIGNALS[signum])
exit_process() exit_process()
def stop_actors_by_class(klass): def stop_actors_by_class(klass):
actors = ActorRegistry.get_by_class(klass) actors = ActorRegistry.get_by_class(klass)
logger.debug(u'Stopping %d instance(s) of %s', len(actors), klass.__name__) logger.debug('Stopping %d instance(s) of %s', len(actors), klass.__name__)
for actor in actors: for actor in actors:
actor.stop() actor.stop()
@ -38,15 +43,15 @@ def stop_remaining_actors():
num_actors = len(ActorRegistry.get_all()) num_actors = len(ActorRegistry.get_all())
while num_actors: while num_actors:
logger.error( logger.error(
u'There are actor threads still running, this is probably a bug') 'There are actor threads still running, this is probably a bug')
logger.debug( logger.debug(
u'Seeing %d actor and %d non-actor thread(s): %s', 'Seeing %d actor and %d non-actor thread(s): %s',
num_actors, threading.active_count() - num_actors, num_actors, threading.active_count() - num_actors,
', '.join([t.name for t in threading.enumerate()])) ', '.join([t.name for t in threading.enumerate()]))
logger.debug(u'Stopping %d actor(s)...', num_actors) logger.debug('Stopping %d actor(s)...', num_actors)
ActorRegistry.stop_all() ActorRegistry.stop_all()
num_actors = len(ActorRegistry.get_all()) num_actors = len(ActorRegistry.get_all())
logger.debug(u'All actors stopped.') logger.debug('All actors stopped.')
class BaseThread(threading.Thread): class BaseThread(threading.Thread):
@ -56,11 +61,11 @@ class BaseThread(threading.Thread):
self.daemon = True self.daemon = True
def run(self): def run(self):
logger.debug(u'%s: Starting thread', self.name) logger.debug('%s: Starting thread', self.name)
try: try:
self.run_inside_try() self.run_inside_try()
except KeyboardInterrupt: except KeyboardInterrupt:
logger.info(u'Interrupted by user') logger.info('Interrupted by user')
except exceptions.SettingsError as e: except exceptions.SettingsError as e:
logger.error(e.message) logger.error(e.message)
except ImportError as e: except ImportError as e:
@ -69,11 +74,12 @@ class BaseThread(threading.Thread):
logger.warning(e) logger.warning(e)
except Exception as e: except Exception as e:
logger.exception(e) logger.exception(e)
logger.debug(u'%s: Exiting thread', self.name) logger.debug('%s: Exiting thread', self.name)
def run_inside_try(self): def run_inside_try(self):
raise NotImplementedError raise NotImplementedError
class DebugThread(threading.Thread): class DebugThread(threading.Thread):
daemon = True daemon = True
name = 'DebugThread' name = 'DebugThread'
@ -81,7 +87,7 @@ class DebugThread(threading.Thread):
event = threading.Event() event = threading.Event()
def handler(self, signum, frame): def handler(self, signum, frame):
logger.info(u'Got %s signal', SIGNALS[signum]) logger.info('Got %s signal', SIGNALS[signum])
self.event.set() self.event.set()
def run(self): def run(self):

View File

@ -1,6 +1,6 @@
# Absolute import needed to import ~/.config/mopidy/settings.py and not # Absolute import needed to import ~/.config/mopidy/settings.py and not
# ourselves # ourselves
from __future__ import absolute_import from __future__ import absolute_import, unicode_literals
import copy import copy
import getpass import getpass
@ -53,11 +53,11 @@ class SettingsProxy(object):
current = self.current # bind locally to avoid copying+updates current = self.current # bind locally to avoid copying+updates
if attr not in current: if attr not in current:
raise exceptions.SettingsError(u'Setting "%s" is not set.' % attr) raise exceptions.SettingsError('Setting "%s" is not set.' % attr)
value = current[attr] value = current[attr]
if isinstance(value, basestring) and len(value) == 0: if isinstance(value, basestring) and len(value) == 0:
raise exceptions.SettingsError(u'Setting "%s" is empty.' % attr) raise exceptions.SettingsError('Setting "%s" is empty.' % attr)
if not value: if not value:
return value return value
if attr.endswith('_PATH') or attr.endswith('_FILE'): if attr.endswith('_PATH') or attr.endswith('_FILE'):
@ -75,17 +75,17 @@ class SettingsProxy(object):
self._read_missing_settings_from_stdin(self.current, self.runtime) self._read_missing_settings_from_stdin(self.current, self.runtime)
if self.get_errors(): if self.get_errors():
logger.error( logger.error(
u'Settings validation errors: %s', 'Settings validation errors: %s',
formatting.indent(self.get_errors_as_string())) formatting.indent(self.get_errors_as_string()))
raise exceptions.SettingsError(u'Settings validation failed.') raise exceptions.SettingsError('Settings validation failed.')
def _read_missing_settings_from_stdin(self, current, runtime): def _read_missing_settings_from_stdin(self, current, runtime):
for setting, value in sorted(current.iteritems()): for setting, value in sorted(current.iteritems()):
if isinstance(value, basestring) and len(value) == 0: if isinstance(value, basestring) and len(value) == 0:
runtime[setting] = self._read_from_stdin(setting + u': ') runtime[setting] = self._read_from_stdin(setting + ': ')
def _read_from_stdin(self, prompt): def _read_from_stdin(self, prompt):
if u'_PASSWORD' in prompt: if '_PASSWORD' in prompt:
return ( return (
getpass.getpass(prompt) getpass.getpass(prompt)
.decode(sys.stdin.encoding, 'ignore')) .decode(sys.stdin.encoding, 'ignore'))
@ -101,7 +101,7 @@ class SettingsProxy(object):
def get_errors_as_string(self): def get_errors_as_string(self):
lines = [] lines = []
for (setting, error) in self.get_errors().iteritems(): for (setting, error) in self.get_errors().iteritems():
lines.append(u'%s: %s' % (setting, error)) lines.append('%s: %s' % (setting, error))
return '\n'.join(lines) return '\n'.join(lines)
@ -151,37 +151,37 @@ def validate_settings(defaults, settings):
for setting, value in settings.iteritems(): for setting, value in settings.iteritems():
if setting in changed: if setting in changed:
if changed[setting] is None: if changed[setting] is None:
errors[setting] = u'Deprecated setting. It may be removed.' errors[setting] = 'Deprecated setting. It may be removed.'
else: else:
errors[setting] = u'Deprecated setting. Use %s.' % ( errors[setting] = 'Deprecated setting. Use %s.' % (
changed[setting],) changed[setting],)
elif setting == 'OUTPUTS': elif setting == 'OUTPUTS':
errors[setting] = ( errors[setting] = (
u'Deprecated setting, please change to OUTPUT. OUTPUT expects ' 'Deprecated setting, please change to OUTPUT. OUTPUT expects '
u'a GStreamer bin description string for your desired output.') 'a GStreamer bin description string for your desired output.')
elif setting == 'SPOTIFY_BITRATE': elif setting == 'SPOTIFY_BITRATE':
if value not in (96, 160, 320): if value not in (96, 160, 320):
errors[setting] = ( errors[setting] = (
u'Unavailable Spotify bitrate. Available bitrates are 96, ' 'Unavailable Spotify bitrate. Available bitrates are 96, '
u'160, and 320.') '160, and 320.')
elif setting.startswith('SHOUTCAST_OUTPUT_'): elif setting.startswith('SHOUTCAST_OUTPUT_'):
errors[setting] = ( errors[setting] = (
u'Deprecated setting, please set the value via the GStreamer ' 'Deprecated setting, please set the value via the GStreamer '
u'bin in OUTPUT.') 'bin in OUTPUT.')
elif setting in list_of_one_or_more: elif setting in list_of_one_or_more:
if not value: if not value:
errors[setting] = u'Must contain at least one value.' errors[setting] = 'Must contain at least one value.'
elif setting not in defaults: elif setting not in defaults:
errors[setting] = u'Unknown setting.' errors[setting] = 'Unknown setting.'
suggestion = did_you_mean(setting, defaults) suggestion = did_you_mean(setting, defaults)
if suggestion: if suggestion:
errors[setting] += u' Did you mean %s?' % suggestion errors[setting] += ' Did you mean %s?' % suggestion
return errors return errors
@ -204,20 +204,20 @@ def format_settings_list(settings):
for (key, value) in sorted(settings.current.iteritems()): for (key, value) in sorted(settings.current.iteritems()):
default_value = settings.default.get(key) default_value = settings.default.get(key)
masked_value = mask_value_if_secret(key, value) masked_value = mask_value_if_secret(key, value)
lines.append(u'%s: %s' % ( lines.append('%s: %s' % (
key, formatting.indent(pprint.pformat(masked_value), places=2))) key, formatting.indent(pprint.pformat(masked_value), places=2)))
if value != default_value and default_value is not None: if value != default_value and default_value is not None:
lines.append( lines.append(
u' Default: %s' % ' Default: %s' %
formatting.indent(pprint.pformat(default_value), places=4)) formatting.indent(pprint.pformat(default_value), places=4))
if errors.get(key) is not None: if errors.get(key) is not None:
lines.append(u' Error: %s' % errors[key]) lines.append(' Error: %s' % errors[key])
return '\n'.join(lines) return '\n'.join(lines)
def mask_value_if_secret(key, value): def mask_value_if_secret(key, value):
if key.endswith('PASSWORD') and value: if key.endswith('PASSWORD') and value:
return u'********' return '********'
else: else:
return value return value

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from subprocess import PIPE, Popen from subprocess import PIPE, Popen
from mopidy import __version__ from mopidy import __version__

View File

@ -2,6 +2,8 @@
Most of this file is taken from the Django project, which is BSD licensed. Most of this file is taken from the Django project, which is BSD licensed.
""" """
from __future__ import unicode_literals
from distutils.core import setup from distutils.core import setup
from distutils.command.install_data import install_data from distutils.command.install_data import install_data
from distutils.command.install import INSTALL_SCHEMES from distutils.command.install import INSTALL_SCHEMES

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import os import os
import sys import sys

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import nose import nose
import yappi import yappi

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy import audio, settings from mopidy import audio, settings
from mopidy.utils.path import path_to_uri from mopidy.utils.path import path_to_uri

View File

@ -0,0 +1 @@
from __future__ import unicode_literals

View File

@ -1,3 +1,6 @@
from __future__ import unicode_literals
def populate_playlist(func): def populate_playlist(func):
def wrapper(self): def wrapper(self):
for track in self.tracks: for track in self.tracks:

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import mock import mock
import random import random
@ -93,18 +95,18 @@ class CurrentPlaylistControllerTest(object):
self.controller.append([Track(uri='z'), track, track]) self.controller.append([Track(uri='z'), track, track])
try: try:
self.controller.get(uri='a') self.controller.get(uri='a')
self.fail(u'Should raise LookupError if multiple matches') self.fail('Should raise LookupError if multiple matches')
except LookupError as e: except LookupError as e:
self.assertEqual(u'"uri=a" match multiple tracks', e[0]) self.assertEqual('"uri=a" match multiple tracks', e[0])
def test_get_by_uri_raises_error_if_no_match(self): def test_get_by_uri_raises_error_if_no_match(self):
self.controller.playlist = Playlist( self.controller.playlist = Playlist(
tracks=[Track(uri='z'), Track(uri='y')]) tracks=[Track(uri='z'), Track(uri='y')])
try: try:
self.controller.get(uri='a') self.controller.get(uri='a')
self.fail(u'Should raise LookupError if no match') self.fail('Should raise LookupError if no match')
except LookupError as e: except LookupError as e:
self.assertEqual(u'"uri=a" match no tracks', e[0]) self.assertEqual('"uri=a" match no tracks', e[0])
def test_get_by_multiple_criteria_returns_elements_matching_all(self): def test_get_by_multiple_criteria_returns_elements_matching_all(self):
track1 = Track(uri='a', name='x') track1 = Track(uri='a', name='x')

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import pykka import pykka
from mopidy import core from mopidy import core

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import mock import mock
import random import random
import time import time

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import os import os
import shutil import shutil
import tempfile import tempfile
@ -31,15 +33,15 @@ class StoredPlaylistsControllerTest(object):
settings.runtime.clear() settings.runtime.clear()
def test_create_returns_playlist_with_name_set(self): def test_create_returns_playlist_with_name_set(self):
playlist = self.stored.create(u'test') playlist = self.stored.create('test')
self.assertEqual(playlist.name, 'test') self.assertEqual(playlist.name, 'test')
def test_create_returns_playlist_with_uri_set(self): def test_create_returns_playlist_with_uri_set(self):
playlist = self.stored.create(u'test') playlist = self.stored.create('test')
self.assert_(playlist.uri) self.assert_(playlist.uri)
def test_create_adds_playlist_to_playlists_collection(self): def test_create_adds_playlist_to_playlists_collection(self):
playlist = self.stored.create(u'test') playlist = self.stored.create('test')
self.assert_(self.stored.playlists) self.assert_(self.stored.playlists)
self.assertIn(playlist, self.stored.playlists) self.assertIn(playlist, self.stored.playlists)
@ -50,7 +52,7 @@ class StoredPlaylistsControllerTest(object):
self.stored.delete('file:///unknown/playlist') self.stored.delete('file:///unknown/playlist')
def test_delete_playlist_removes_it_from_the_collection(self): def test_delete_playlist_removes_it_from_the_collection(self):
playlist = self.stored.create(u'test') playlist = self.stored.create('test')
self.assertIn(playlist, self.stored.playlists) self.assertIn(playlist, self.stored.playlists)
self.stored.delete(playlist.uri) self.stored.delete(playlist.uri)
@ -66,7 +68,7 @@ class StoredPlaylistsControllerTest(object):
self.assertRaises(LookupError, test) self.assertRaises(LookupError, test)
def test_get_with_right_criteria(self): def test_get_with_right_criteria(self):
playlist1 = self.stored.create(u'test') playlist1 = self.stored.create('test')
playlist2 = self.stored.get(name='test') playlist2 = self.stored.get(name='test')
self.assertEqual(playlist1, playlist2) self.assertEqual(playlist1, playlist2)
@ -82,21 +84,21 @@ class StoredPlaylistsControllerTest(object):
playlist, Playlist(name='a'), Playlist(name='b')] playlist, Playlist(name='a'), Playlist(name='b')]
try: try:
self.stored.get(name='b') self.stored.get(name='b')
self.fail(u'Should raise LookupError if multiple matches') self.fail('Should raise LookupError if multiple matches')
except LookupError as e: except LookupError as e:
self.assertEqual(u'"name=b" match multiple playlists', e[0]) self.assertEqual('"name=b" match multiple playlists', e[0])
def test_get_by_name_raises_keyerror_if_no_match(self): def test_get_by_name_raises_keyerror_if_no_match(self):
self.backend.stored_playlists.playlists = [ self.backend.stored_playlists.playlists = [
Playlist(name='a'), Playlist(name='b')] Playlist(name='a'), Playlist(name='b')]
try: try:
self.stored.get(name='c') self.stored.get(name='c')
self.fail(u'Should raise LookupError if no match') self.fail('Should raise LookupError if no match')
except LookupError as e: except LookupError as e:
self.assertEqual(u'"name=c" match no playlists', e[0]) self.assertEqual('"name=c" match no playlists', e[0])
def test_lookup_finds_playlist_by_uri(self): def test_lookup_finds_playlist_by_uri(self):
original_playlist = self.stored.create(u'test') original_playlist = self.stored.create('test')
looked_up_playlist = self.stored.lookup(original_playlist.uri) looked_up_playlist = self.stored.lookup(original_playlist.uri)
@ -107,10 +109,10 @@ class StoredPlaylistsControllerTest(object):
pass pass
def test_save_replaces_stored_playlist_with_updated_playlist(self): def test_save_replaces_stored_playlist_with_updated_playlist(self):
playlist1 = self.stored.create(u'test1') playlist1 = self.stored.create('test1')
self.assertIn(playlist1, self.stored.playlists) self.assertIn(playlist1, self.stored.playlists)
playlist2 = playlist1.copy(name=u'test2') playlist2 = playlist1.copy(name='test2')
playlist2 = self.stored.save(playlist2) playlist2 = self.stored.save(playlist2)
self.assertNotIn(playlist1, self.stored.playlists) self.assertNotIn(playlist1, self.stored.playlists)
self.assertIn(playlist2, self.stored.playlists) self.assertIn(playlist2, self.stored.playlists)

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import mock import mock
import pykka import pykka

View File

@ -1,6 +1,9 @@
from __future__ import unicode_literals
from mopidy.utils.path import path_to_uri from mopidy.utils.path import path_to_uri
from tests import path_to_data_dir from tests import path_to_data_dir
song = path_to_data_dir('song%s.wav') song = path_to_data_dir('song%s.wav')
generate_song = lambda i: path_to_uri(song % i) generate_song = lambda i: path_to_uri(song % i)

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy import settings from mopidy import settings
from mopidy.backends.local import LocalBackend from mopidy.backends.local import LocalBackend
from mopidy.models import Track from mopidy.models import Track

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy import settings from mopidy import settings
from mopidy.backends.local import LocalBackend from mopidy.backends.local import LocalBackend

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy import settings from mopidy import settings
from mopidy.backends.local import LocalBackend from mopidy.backends.local import LocalBackend
from mopidy.core import PlaybackState from mopidy.core import PlaybackState

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import os import os
from mopidy import settings from mopidy import settings
@ -20,38 +22,38 @@ class LocalStoredPlaylistsControllerTest(
path = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test.m3u') path = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test.m3u')
self.assertFalse(os.path.exists(path)) self.assertFalse(os.path.exists(path))
self.stored.create(u'test') self.stored.create('test')
self.assertTrue(os.path.exists(path)) self.assertTrue(os.path.exists(path))
def test_create_slugifies_playlist_name(self): def test_create_slugifies_playlist_name(self):
path = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test-foo-bar.m3u') path = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test-foo-bar.m3u')
self.assertFalse(os.path.exists(path)) self.assertFalse(os.path.exists(path))
playlist = self.stored.create(u'test FOO baR') playlist = self.stored.create('test FOO baR')
self.assertEqual(u'test-foo-bar', playlist.name) self.assertEqual('test-foo-bar', playlist.name)
self.assertTrue(os.path.exists(path)) self.assertTrue(os.path.exists(path))
def test_create_slugifies_names_which_tries_to_change_directory(self): 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(settings.LOCAL_PLAYLIST_PATH, 'test-foo-bar.m3u')
self.assertFalse(os.path.exists(path)) self.assertFalse(os.path.exists(path))
playlist = self.stored.create(u'../../test FOO baR') playlist = self.stored.create('../../test FOO baR')
self.assertEqual(u'test-foo-bar', playlist.name) self.assertEqual('test-foo-bar', playlist.name)
self.assertTrue(os.path.exists(path)) self.assertTrue(os.path.exists(path))
def test_saved_playlist_is_persisted(self): def test_saved_playlist_is_persisted(self):
path1 = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test1.m3u') path1 = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test1.m3u')
path2 = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test2-foo-bar.m3u') path2 = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test2-foo-bar.m3u')
playlist = self.stored.create(u'test1') playlist = self.stored.create('test1')
self.assertTrue(os.path.exists(path1)) self.assertTrue(os.path.exists(path1))
self.assertFalse(os.path.exists(path2)) self.assertFalse(os.path.exists(path2))
playlist = playlist.copy(name=u'test2 FOO baR') playlist = playlist.copy(name='test2 FOO baR')
playlist = self.stored.save(playlist) playlist = self.stored.save(playlist)
self.assertEqual(u'test2-foo-bar', playlist.name) self.assertEqual('test2-foo-bar', playlist.name)
self.assertFalse(os.path.exists(path1)) self.assertFalse(os.path.exists(path1))
self.assertTrue(os.path.exists(path2)) self.assertTrue(os.path.exists(path2))
@ -59,7 +61,7 @@ class LocalStoredPlaylistsControllerTest(
path = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test.m3u') path = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test.m3u')
self.assertFalse(os.path.exists(path)) self.assertFalse(os.path.exists(path))
playlist = self.stored.create(u'test') playlist = self.stored.create('test')
self.assertTrue(os.path.exists(path)) self.assertTrue(os.path.exists(path))
self.stored.delete(playlist.uri) self.stored.delete(playlist.uri)
@ -68,7 +70,7 @@ class LocalStoredPlaylistsControllerTest(
def test_playlist_contents_is_written_to_disk(self): def test_playlist_contents_is_written_to_disk(self):
track = Track(uri=generate_song(1)) track = Track(uri=generate_song(1))
track_path = track.uri[len('file://'):] track_path = track.uri[len('file://'):]
playlist = self.stored.create(u'test') playlist = self.stored.create('test')
playlist_path = playlist.uri[len('file://'):] playlist_path = playlist.uri[len('file://'):]
playlist = playlist.copy(tracks=[track]) playlist = playlist.copy(tracks=[track])
playlist = self.stored.save(playlist) playlist = self.stored.save(playlist)
@ -82,7 +84,7 @@ class LocalStoredPlaylistsControllerTest(
playlist_path = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test.m3u') playlist_path = os.path.join(settings.LOCAL_PLAYLIST_PATH, 'test.m3u')
track = Track(uri=path_to_uri(path_to_data_dir('uri2'))) track = Track(uri=path_to_uri(path_to_data_dir('uri2')))
playlist = self.stored.create(u'test') playlist = self.stored.create('test')
playlist = playlist.copy(tracks=[track]) playlist = playlist.copy(tracks=[track])
playlist = self.stored.save(playlist) playlist = self.stored.save(playlist)

View File

@ -1,5 +1,7 @@
# encoding: utf-8 # encoding: utf-8
from __future__ import unicode_literals
import os import os
import tempfile import tempfile
@ -12,7 +14,7 @@ from tests import unittest, path_to_data_dir
data_dir = path_to_data_dir('') data_dir = path_to_data_dir('')
song1_path = path_to_data_dir('song1.mp3') song1_path = path_to_data_dir('song1.mp3')
song2_path = path_to_data_dir('song2.mp3') song2_path = path_to_data_dir('song2.mp3')
encoded_path = path_to_data_dir(u'æøå.mp3') encoded_path = path_to_data_dir('æøå.mp3')
song1_uri = path_to_uri(song1_path) song1_uri = path_to_uri(song1_path)
song2_uri = path_to_uri(song2_path) song2_uri = path_to_uri(song2_path)
encoded_uri = path_to_uri(encoded_path) encoded_uri = path_to_uri(encoded_path)
@ -138,10 +140,10 @@ class MPDTagCacheToTracksTest(unittest.TestCase):
path_to_data_dir('utf8_tag_cache'), path_to_data_dir('')) path_to_data_dir('utf8_tag_cache'), path_to_data_dir(''))
uri = path_to_uri(path_to_data_dir('song1.mp3')) uri = path_to_uri(path_to_data_dir('song1.mp3'))
artists = [Artist(name=u'æøå')] artists = [Artist(name='æøå')]
album = Album(name=u'æøå', artists=artists) album = Album(name='æøå', artists=artists)
track = Track( track = Track(
uri=uri, name=u'æøå', artists=artists, album=album, length=4000) uri=uri, name='æøå', artists=artists, album=album, length=4000)
self.assertEqual(track, list(tracks)[0]) self.assertEqual(track, list(tracks)[0])

View File

@ -0,0 +1 @@
from __future__ import unicode_literals

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import mock import mock
import pykka import pykka
@ -26,8 +28,8 @@ class CoreActorTest(unittest.TestCase):
self.assertIn('dummy2', result) self.assertIn('dummy2', result)
def test_backends_with_colliding_uri_schemes_fails(self): def test_backends_with_colliding_uri_schemes_fails(self):
self.backend1.__class__.__name__ = 'B1' self.backend1.__class__.__name__ = b'B1'
self.backend2.__class__.__name__ = 'B2' self.backend2.__class__.__name__ = b'B2'
self.backend2.uri_schemes.get.return_value = ['dummy1', 'dummy2'] self.backend2.uri_schemes.get.return_value = ['dummy1', 'dummy2']
self.assertRaisesRegexp( self.assertRaisesRegexp(
AssertionError, AssertionError,

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import mock import mock
from mopidy.backends import base from mopidy.backends import base

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy.core import CoreListener, PlaybackState from mopidy.core import CoreListener, PlaybackState
from mopidy.models import Track from mopidy.models import Track

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import mock import mock
from mopidy.backends import base from mopidy.backends import base

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import mock import mock
from mopidy.backends import base from mopidy.backends import base

View File

@ -0,0 +1 @@
from __future__ import unicode_literals

Some files were not shown because too many files have changed in this diff Show More