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
from __future__ import unicode_literals
import sys
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.frontends.mpd.translator import tracks_to_tag_cache_format
setup_root_logger()
setup_console_logging(2)
tracks = []
def store(data):
track = translator(data)
tracks.append(track)
logging.debug(u'Added %s', track.uri)
logging.debug('Added %s', track.uri)
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)
try:
@ -29,10 +37,12 @@ try:
except KeyboardInterrupt:
scanner.stop()
logging.info(u'Done')
logging.info('Done')
for a in tracks_to_tag_cache_format(tracks):
if len(a) == 1:
print (u'%s' % a).encode('utf-8')
print ('%s' % a).encode('utf-8')
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
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**
- :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
# serve to show the default.
from __future__ import unicode_literals
import os
import sys
@ -89,8 +91,8 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = u'Mopidy'
copyright = u'2010-2012, Stein Magnus Jodal and contributors'
project = 'Mopidy'
copyright = '2010-2012, Stein Magnus Jodal and contributors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@ -231,8 +233,8 @@ latex_documents = [
(
'index',
'Mopidy.tex',
u'Mopidy Documentation',
u'Stein Magnus Jodal',
'Mopidy Documentation',
'Stein Magnus Jodal',
'manual'
),
]

View File

@ -84,6 +84,22 @@ contributing.
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
<http://pypi.python.org/pypi/pep8/>`_ or `flake8
<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
from distutils.version import StrictVersion as SV
# pylint: enable = E0611,F0401
@ -9,13 +11,13 @@ import pykka
if not (2, 6) <= sys.version_info < (3,):
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])))
if (isinstance(pykka.__version__, basestring)
and not SV('1.0') <= SV(pykka.__version__) < SV('2.0')):
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')

View File

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

View File

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

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import pygst
pygst.require('0.10')
import gst
@ -70,9 +72,9 @@ class Audio(pykka.ThreadingActor):
# These caps matches the audio data provided by libspotify
default_caps = gst.Caps(
'audio/x-raw-int, endianness=(int)1234, channels=(int)2, '
'width=(int)16, depth=(int)16, signed=(boolean)true, '
'rate=(int)44100')
b'audio/x-raw-int, endianness=(int)1234, channels=(int)2, '
b'width=(int)16, depth=(int)16, signed=(boolean)true, '
b'rate=(int)44100')
source = element.get_property('source')
source.set_property('caps', default_caps)
@ -109,7 +111,7 @@ class Audio(pykka.ThreadingActor):
return
# 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:
logger.warning(
'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()
elif message.type == gst.MESSAGE_ERROR:
error, debug = message.parse_error()
logger.error(u'%s %s', error, debug)
logger.error('%s %s', error, debug)
self.stop_playback()
elif message.type == gst.MESSAGE_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):
logger.debug(u'Triggering reached end of stream event')
logger.debug('Triggering reached end of stream event')
AudioListener.send('reached_end_of_stream')
def set_uri(self, uri):
@ -389,12 +391,12 @@ class Audio(pykka.ThreadingActor):
# Default to blank data to trick shoutcast into clearing any previous
# values it might have.
taglist[gst.TAG_ARTIST] = u' '
taglist[gst.TAG_TITLE] = u' '
taglist[gst.TAG_ALBUM] = u' '
taglist[gst.TAG_ARTIST] = ' '
taglist[gst.TAG_TITLE] = ' '
taglist[gst.TAG_ALBUM] = ' '
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:
taglist[gst.TAG_TITLE] = track.name

View File

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

View File

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

View File

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

View File

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

View File

@ -45,6 +45,8 @@ Configuration examples::
u'source=aux speakers-a=on speakers-b=off')
"""
from __future__ import unicode_literals
import logging
import pygst
@ -107,7 +109,7 @@ class NadMixer(gst.Element, gst.ImplementsInterface, gst.interfaces.Mixer):
def do_change_state(self, transition):
if transition == gst.STATE_CHANGE_NULL_TO_READY:
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
self._start_nad_talker()
return gst.STATE_CHANGE_SUCCESS
@ -164,7 +166,7 @@ class NadTalker(pykka.ThreadingActor):
self._set_device_to_known_state()
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(
port=self.port,
baudrate=self.BAUDRATE,
@ -183,7 +185,7 @@ class NadTalker(pykka.ThreadingActor):
def _get_device_model(self):
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
def _power_device_on(self):
@ -212,19 +214,19 @@ class NadTalker(pykka.ThreadingActor):
if current_nad_volume is None:
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
if self._decrease_volume():
current_nad_volume -= 1
if current_nad_volume == 0:
logger.info(u'NAD amplifier: Done calibrating')
logger.info('NAD amplifier: Done calibrating')
else:
self.actor_ref.proxy().calibrate_volume(current_nad_volume)
def set_volume(self, volume):
# Increase or decrease the amplifier volume until it matches the given
# 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))
if self._nad_volume is None:
return # Calibration needed
@ -250,12 +252,12 @@ class NadTalker(pykka.ThreadingActor):
if self._ask_device(key) == value:
return
logger.info(
u'NAD amplifier: Setting "%s" to "%s" (attempt %d/3)',
'NAD amplifier: Setting "%s" to "%s" (attempt %d/3)',
key, value, attempt)
self._command_device(key, value)
if self._ask_device(key) != value:
logger.info(
u'NAD amplifier: Gave up on setting "%s" to "%s"',
'NAD amplifier: Gave up on setting "%s" to "%s"',
key, value)
def _ask_device(self, key):

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import pygst
pygst.require('0.10')
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

View File

@ -14,6 +14,8 @@ The backend handles URIs starting with ``dummy:``.
- None
"""
from __future__ import unicode_literals
import pykka
from mopidy.backends import base
@ -28,7 +30,7 @@ class DummyBackend(pykka.ThreadingActor, base.Backend):
self.playback = DummyPlaybackProvider(audio=audio, backend=self)
self.stored_playlists = DummyStoredPlaylistsProvider(backend=self)
self.uri_schemes = [u'dummy']
self.uri_schemes = ['dummy']
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`
"""
from __future__ import unicode_literals
# flake8: noqa
from .actor import LocalBackend

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging
import os
import threading
@ -50,14 +52,14 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager):
def logged_in(self, session, error):
"""Callback used by pyspotify"""
if error:
logger.error(u'Spotify login error: %s', error)
logger.error('Spotify login error: %s', error)
return
logger.info(u'Connected to Spotify')
logger.info('Connected to Spotify')
self.session = session
logger.debug(
u'Preferred Spotify bitrate is %s kbps',
'Preferred Spotify bitrate is %s kbps',
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):
"""Callback used by pyspotify"""
logger.info(u'Disconnected from Spotify')
logger.info('Disconnected from Spotify')
def metadata_updated(self, session):
"""Callback used by pyspotify"""
logger.debug(u'Callback called: Metadata updated')
logger.debug('Callback called: Metadata updated')
def connection_error(self, session, error):
"""Callback used by pyspotify"""
if error is None:
logger.info(u'Spotify connection OK')
logger.info('Spotify connection OK')
else:
logger.error(u'Spotify connection error: %s', error)
logger.error('Spotify connection error: %s', error)
self.backend.playback.pause()
def message_to_user(self, session, message):
"""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,
sample_type, sample_rate, channels):
"""Callback used by pyspotify"""
# pylint: disable = R0913
# 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 = """
audio/x-raw-int,
endianness=(int)1234,
@ -111,12 +113,12 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager):
def play_token_lost(self, session):
"""Callback used by pyspotify"""
logger.debug(u'Play token lost')
logger.debug('Play token lost')
self.backend.playback.pause()
def log_message(self, session, data):
"""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:
# XXX This is a very very fragile and ugly hack, but we get no
# 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):
"""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()
def refresh_stored_playlists(self):
"""Refresh the stored playlists in the backend with fresh meta data
from Spotify"""
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
playlists = map(
translator.to_mopidy_playlist, self.session.playlist_container())
playlists = filter(None, 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):
"""Search method used by Mopidy backend"""
@ -161,6 +163,6 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager):
def logout(self):
"""Log out from spotify"""
logger.debug(u'Logging out from Spotify')
logger.debug('Logging out from Spotify')
if self.session:
self.session.logout()

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from copy import copy
import logging
import random
@ -73,7 +75,7 @@ class CurrentPlaylistController(object):
was added to the current playlist playlist
"""
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)
if at_position is not None:
self._cp_tracks.insert(at_position, cp_track)
@ -132,9 +134,9 @@ class CurrentPlaylistController(object):
criteria_string = ', '.join(
['%s=%s' % (k, v) for (k, v) in criteria.iteritems()])
if len(matches) == 0:
raise LookupError(u'"%s" match no tracks' % criteria_string)
raise LookupError('"%s" match no tracks' % criteria_string)
else:
raise LookupError(u'"%s" match multiple tracks' % criteria_string)
raise LookupError('"%s" match multiple tracks' % criteria_string)
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]]
def _trigger_playlist_changed(self):
logger.debug(u'Triggering playlist changed event')
logger.debug('Triggering playlist changed event')
listener.CoreListener.send('playlist_changed')

View File

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

View File

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

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging
import random
import urlparse
@ -28,13 +30,13 @@ class PlaybackState(object):
"""
#: Constant representing the paused state.
PAUSED = u'paused'
PAUSED = 'paused'
#: Constant representing the playing state.
PLAYING = u'playing'
PLAYING = 'playing'
#: Constant representing the stopped state.
STOPPED = u'stopped'
STOPPED = 'stopped'
class PlaybackController(object):
@ -290,7 +292,7 @@ class PlaybackController(object):
@state.setter # noqa
def state(self, 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)
@ -493,7 +495,7 @@ class PlaybackController(object):
self.current_cp_track = None
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:
return
listener.CoreListener.send(
@ -501,7 +503,7 @@ class PlaybackController(object):
track=self.current_track, time_position=self.time_position)
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:
return
listener.CoreListener.send(
@ -509,14 +511,14 @@ class PlaybackController(object):
track=self.current_track, time_position=self.time_position)
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:
return
listener.CoreListener.send(
'track_playback_started', track=self.current_track)
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:
return
listener.CoreListener.send(
@ -524,15 +526,15 @@ class PlaybackController(object):
track=self.current_track, time_position=self.time_position)
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(
'playback_state_changed',
old_state=old_state, new_state=new_state)
def _trigger_options_changed(self):
logger.debug(u'Triggering options changed event')
logger.debug('Triggering options changed event')
listener.CoreListener.send('options_changed')
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)

View File

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

View File

@ -1,3 +1,6 @@
from __future__ import unicode_literals
class MopidyException(Exception):
def __init__(self, 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.
"""
from __future__ import unicode_literals
import logging
import time
@ -54,21 +56,21 @@ class LastfmFrontend(pykka.ThreadingActor, CoreListener):
self.lastfm = pylast.LastFMNetwork(
api_key=API_KEY, api_secret=API_SECRET,
username=username, password_hash=password_hash)
logger.info(u'Connected to Last.fm')
logger.info('Connected to Last.fm')
except exceptions.SettingsError as e:
logger.info(u'Last.fm scrobbler not started')
logger.debug(u'Last.fm settings error: %s', e)
logger.info('Last.fm scrobbler not started')
logger.debug('Last.fm settings error: %s', e)
self.stop()
except (pylast.NetworkError, pylast.MalformedResponseError,
pylast.WSError) as e:
logger.error(u'Error during Last.fm setup: %s', e)
logger.error('Error during Last.fm setup: %s', e)
self.stop()
def track_playback_started(self, track):
artists = ', '.join([a.name for a in track.artists])
duration = track.length and track.length // 1000 or 0
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:
self.lastfm.update_now_playing(
artists,
@ -79,22 +81,22 @@ class LastfmFrontend(pykka.ThreadingActor, CoreListener):
mbid=(track.musicbrainz_id or ''))
except (pylast.ScrobblingError, pylast.NetworkError,
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):
artists = ', '.join([a.name for a in track.artists])
duration = track.length and track.length // 1000 or 0
time_position = time_position // 1000
if duration < 30:
logger.debug(u'Track too short to scrobble. (30s)')
logger.debug('Track too short to scrobble. (30s)')
return
if time_position < duration // 2 and time_position < 240:
logger.debug(
u'Track not played long enough to scrobble. (50% or 240s)')
'Track not played long enough to scrobble. (50% or 240s)')
return
if self.last_start_time is None:
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:
self.lastfm.scrobble(
artists,
@ -106,4 +108,4 @@ class LastfmFrontend(pykka.ThreadingActor, CoreListener):
mbid=(track.musicbrainz_id or ''))
except (pylast.ScrobblingError, pylast.NetworkError,
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.
"""
from __future__ import unicode_literals
# flake8: noqa
from .actor import MpdFrontend

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging
import sys
@ -24,11 +26,11 @@ class MpdFrontend(pykka.ThreadingActor, CoreListener):
max_connections=settings.MPD_SERVER_MAX_CONNECTIONS)
except IOError as error:
logger.error(
u'MPD server startup failed: %s',
'MPD server startup failed: %s',
encoding.locale_decode(error))
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):
process.stop_actors_by_class(session.MpdSession)

View File

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

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy.exceptions import MopidyException
@ -19,7 +21,7 @@ class MpdAckError(MopidyException):
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)
self.message = message
self.index = index
@ -31,7 +33,7 @@ class MpdAckError(MopidyException):
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)
@ -48,7 +50,7 @@ class MpdPermissionError(MpdAckError):
def __init__(self, *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):
@ -56,8 +58,8 @@ class MpdUnknownCommand(MpdAckError):
def __init__(self, *args, **kwargs):
super(MpdUnknownCommand, self).__init__(*args, **kwargs)
self.message = u'unknown command "%s"' % self.command
self.command = u''
self.message = 'unknown command "%s"' % self.command
self.command = ''
class MpdNoExistError(MpdAckError):
@ -73,4 +75,4 @@ class MpdNotImplemented(MpdAckError):
def __init__(self, *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>`_.
"""
from __future__ import unicode_literals
from collections import namedtuple
import re
#: 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.
LINE_TERMINATOR = u'\n'
LINE_TERMINATOR = '\n'
#: The MPD protocol version is 0.16.0.
VERSION = u'0.16.0'
VERSION = '0.16.0'
MpdCommand = namedtuple('MpdCommand', ['name', 'auth_required'])
@ -55,7 +57,7 @@ def handle_request(pattern, auth_required=True):
mpd_commands.add(
MpdCommand(name=match.group(), auth_required=auth_required))
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))
request_handlers[pattern] = func
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.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.exceptions import MpdUnknownCommand
@ -40,10 +42,10 @@ def command_list_end(context):
command, current_command_list_index=index)
command_list_response.extend(response)
if (command_list_response and
command_list_response[-1].startswith(u'ACK')):
command_list_response[-1].startswith('ACK')):
return command_list_response
if command_list_ok:
command_list_response.append(u'list_OK')
command_list_response.append('list_OK')
return command_list_response

View File

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

View File

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

View File

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

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy.core import PlaybackState
from mopidy.frontends.mpd.protocol import handle_request
from mopidy.frontends.mpd.exceptions import (
@ -153,7 +155,7 @@ def playid(context, cpid):
cp_track = context.core.current_playlist.get(cpid=cpid).get()
return context.core.playback.play(cp_track).get()
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+)$')
@ -187,7 +189,7 @@ def playpos(context, songpos):
songpos, songpos + 1).get()[0]
return context.core.playback.play(cp_track).get()
except IndexError:
raise MpdArgError(u'Bad song index', command=u'play')
raise MpdArgError('Bad song index', command='play')
def _play_minus_one(context):
@ -311,7 +313,7 @@ def replay_gain_status(context):
Prints replay gain options. Currently, only the variable
``replay_gain_mode`` is returned.
"""
return u'off' # TODO
return 'off' # TODO
@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.exceptions import MpdNotImplemented
@ -93,5 +95,5 @@ def urlhandlers(context):
Gets a list of available URL handlers.
"""
return [
(u'handler', uri_scheme)
('handler', uri_scheme)
for uri_scheme in context.core.uri_schemes.get()]

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import os
import re
@ -93,7 +95,7 @@ def artists_to_mpd_format(artists):
"""
artists = list(artists)
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):
@ -178,7 +180,7 @@ def _add_to_tag_cache(result, folders, files):
def tracks_to_directory_tree(tracks):
directories = ({}, [])
for track in tracks:
path = u''
path = ''
current = directories
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')
"""
from __future__ import unicode_literals
# flake8: noqa
from .actor import MprisFrontend

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging
import pykka
@ -12,7 +14,7 @@ try:
import indicate
except ImportError as import_error:
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):
@ -27,20 +29,20 @@ class MprisFrontend(pykka.ThreadingActor, CoreListener):
self.mpris_object = objects.MprisObject(self.core)
self._send_startup_notification()
except Exception as e:
logger.error(u'MPRIS frontend setup failed (%s)', e)
logger.error('MPRIS frontend setup failed (%s)', e)
self.stop()
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:
self.mpris_object.remove_from_connection()
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):
"""
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
running. When Mopidy exits, the server will be unreferenced and Mopidy
@ -48,12 +50,12 @@ class MprisFrontend(pykka.ThreadingActor, CoreListener):
"""
if not indicate:
return
logger.debug(u'Sending startup notification...')
logger.debug('Sending startup notification...')
self.indicate_server = indicate.Server()
self.indicate_server.set_type('music.mopidy')
self.indicate_server.set_desktop_file(settings.DESKTOP_FILE)
self.indicate_server.show()
logger.debug(u'Startup notification sent')
logger.debug('Startup notification sent')
def _emit_properties_changed(self, *changed_properties):
if self.mpris_object is None:
@ -65,25 +67,25 @@ class MprisFrontend(pykka.ThreadingActor, CoreListener):
objects.PLAYER_IFACE, dict(props_with_new_values), [])
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')
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')
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')
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')
def volume_changed(self):
logger.debug(u'Received volume changed event')
logger.debug('Received volume changed event')
self._emit_properties_changed('Volume')
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)

View File

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

View File

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

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import gobject
gobject.threads_init()
@ -62,7 +64,7 @@ class Scanner(object):
fakesink = gst.element_factory_make('fakesink')
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(
'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.
"""
from __future__ import unicode_literals
#: List of playback backends to use. See :ref:`backend-implementations` for all
#: available backends.
#:
@ -20,21 +22,21 @@ All available settings and their default values.
#: u'mopidy.backends.spotify.SpotifyBackend',
#: )
BACKENDS = (
u'mopidy.backends.local.LocalBackend',
u'mopidy.backends.spotify.SpotifyBackend',
'mopidy.backends.local.LocalBackend',
'mopidy.backends.spotify.SpotifyBackend',
)
#: The log format used for informational logging.
#:
#: See http://docs.python.org/2/library/logging.html#formatter-objects for
#: details on the format.
CONSOLE_LOG_FORMAT = u'%(levelname)-8s %(message)s'
CONSOLE_LOG_FORMAT = '%(levelname)-8s %(message)s'
#: The log format used for debug logging.
#:
#: See http://docs.python.org/library/logging.html#formatter-objects for
#: details on the format.
DEBUG_LOG_FORMAT = u'%(levelname)-8s %(asctime)s' + \
DEBUG_LOG_FORMAT = '%(levelname)-8s %(asctime)s' + \
' [%(process)d:%(threadName)s] %(name)s\n %(message)s'
#: The file to dump debug log data to when Mopidy is run with the
@ -43,13 +45,13 @@ DEBUG_LOG_FORMAT = u'%(levelname)-8s %(asctime)s' + \
#: Default::
#:
#: 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
#: get a SIGUSR1. Mainly a debug tool for figuring out deadlocks.
#:
#: Default::
#:
#:
#: DEBUG_THREAD = False
DEBUG_THREAD = False
@ -60,7 +62,7 @@ DEBUG_THREAD = False
#: Default::
#:
#: 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
#: available frontends.
@ -73,20 +75,20 @@ DESKTOP_FILE = u'/usr/share/applications/mopidy.desktop'
#: u'mopidy.frontends.mpris.MprisFrontend',
#: )
FRONTENDS = (
u'mopidy.frontends.mpd.MpdFrontend',
u'mopidy.frontends.lastfm.LastfmFrontend',
u'mopidy.frontends.mpris.MprisFrontend',
'mopidy.frontends.mpd.MpdFrontend',
'mopidy.frontends.lastfm.LastfmFrontend',
'mopidy.frontends.mpris.MprisFrontend',
)
#: Your `Last.fm <http://www.last.fm/>`_ username.
#:
#: Used by :mod:`mopidy.frontends.lastfm`.
LASTFM_USERNAME = u''
LASTFM_USERNAME = ''
#: Your `Last.fm <http://www.last.fm/>`_ password.
#:
#: Used by :mod:`mopidy.frontends.lastfm`.
LASTFM_PASSWORD = u''
LASTFM_PASSWORD = ''
#: Path to folder with local music.
#:
@ -95,7 +97,7 @@ LASTFM_PASSWORD = u''
#: Default::
#:
#: 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.
#:
@ -104,7 +106,7 @@ LOCAL_MUSIC_PATH = u'$XDG_MUSIC_DIR'
#: Default::
#:
#: 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.
#:
@ -113,7 +115,7 @@ LOCAL_PLAYLIST_PATH = u'$XDG_DATA_DIR/mopidy/playlists'
#: 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 = '$XDG_DATA_DIR/mopidy/tag_cache'
#: Audio mixer to use.
#:
@ -126,7 +128,7 @@ LOCAL_TAG_CACHE_FILE = u'$XDG_DATA_DIR/mopidy/tag_cache'
#: Default::
#:
#: MIXER = u'autoaudiomixer'
MIXER = u'autoaudiomixer'
MIXER = 'autoaudiomixer'
#: Audio mixer track to use.
#:
@ -153,7 +155,7 @@ MIXER_TRACK = None
#: Listens on all IPv4 interfaces.
#: ``::``
#: 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.
#:
@ -185,7 +187,7 @@ MPD_SERVER_MAX_CONNECTIONS = 20
#: Default::
#:
#: OUTPUT = u'autoaudiosink'
OUTPUT = u'autoaudiosink'
OUTPUT = 'autoaudiosink'
#: Path to the Spotify cache.
#:
@ -194,17 +196,17 @@ OUTPUT = u'autoaudiosink'
#: Default::
#:
#: 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.
#:
#: Used by :mod:`mopidy.backends.spotify`.
SPOTIFY_USERNAME = u''
SPOTIFY_USERNAME = ''
#: Your Spotify Premium password.
#:
#: Used by :mod:`mopidy.backends.spotify`.
SPOTIFY_PASSWORD = u''
SPOTIFY_PASSWORD = ''
#: 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 platform
import sys

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import logging
import signal
import sys
@ -10,26 +12,29 @@ from pykka.registry import ActorRegistry
from mopidy import exceptions
logger = logging.getLogger('mopidy.utils.process')
SIGNALS = dict((k, v) for v, k in signal.__dict__.iteritems()
if v.startswith('SIG') and not v.startswith('SIG_'))
def exit_process():
logger.debug(u'Interrupting main...')
logger.debug('Interrupting main...')
thread.interrupt_main()
logger.debug(u'Interrupted main')
logger.debug('Interrupted main')
def exit_handler(signum, frame):
"""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()
def stop_actors_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:
actor.stop()
@ -38,15 +43,15 @@ def stop_remaining_actors():
num_actors = len(ActorRegistry.get_all())
while num_actors:
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(
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,
', '.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()
num_actors = len(ActorRegistry.get_all())
logger.debug(u'All actors stopped.')
logger.debug('All actors stopped.')
class BaseThread(threading.Thread):
@ -56,11 +61,11 @@ class BaseThread(threading.Thread):
self.daemon = True
def run(self):
logger.debug(u'%s: Starting thread', self.name)
logger.debug('%s: Starting thread', self.name)
try:
self.run_inside_try()
except KeyboardInterrupt:
logger.info(u'Interrupted by user')
logger.info('Interrupted by user')
except exceptions.SettingsError as e:
logger.error(e.message)
except ImportError as e:
@ -69,11 +74,12 @@ class BaseThread(threading.Thread):
logger.warning(e)
except Exception as e:
logger.exception(e)
logger.debug(u'%s: Exiting thread', self.name)
logger.debug('%s: Exiting thread', self.name)
def run_inside_try(self):
raise NotImplementedError
class DebugThread(threading.Thread):
daemon = True
name = 'DebugThread'
@ -81,7 +87,7 @@ class DebugThread(threading.Thread):
event = threading.Event()
def handler(self, signum, frame):
logger.info(u'Got %s signal', SIGNALS[signum])
logger.info('Got %s signal', SIGNALS[signum])
self.event.set()
def run(self):

View File

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

View File

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

View File

@ -2,6 +2,8 @@
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.command.install_data import install_data
from distutils.command.install import INSTALL_SCHEMES

View File

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

View File

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

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from mopidy import audio, settings
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 wrapper(self):
for track in self.tracks:

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import mock
import random
@ -93,18 +95,18 @@ class CurrentPlaylistControllerTest(object):
self.controller.append([Track(uri='z'), track, track])
try:
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:
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):
self.controller.playlist = Playlist(
tracks=[Track(uri='z'), Track(uri='y')])
try:
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:
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):
track1 = Track(uri='a', name='x')

View File

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

View File

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

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import os
import shutil
import tempfile
@ -31,15 +33,15 @@ class StoredPlaylistsControllerTest(object):
settings.runtime.clear()
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')
def test_create_returns_playlist_with_uri_set(self):
playlist = self.stored.create(u'test')
playlist = self.stored.create('test')
self.assert_(playlist.uri)
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.assertIn(playlist, self.stored.playlists)
@ -50,7 +52,7 @@ class StoredPlaylistsControllerTest(object):
self.stored.delete('file:///unknown/playlist')
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.stored.delete(playlist.uri)
@ -66,7 +68,7 @@ class StoredPlaylistsControllerTest(object):
self.assertRaises(LookupError, test)
def test_get_with_right_criteria(self):
playlist1 = self.stored.create(u'test')
playlist1 = self.stored.create('test')
playlist2 = self.stored.get(name='test')
self.assertEqual(playlist1, playlist2)
@ -82,21 +84,21 @@ class StoredPlaylistsControllerTest(object):
playlist, Playlist(name='a'), Playlist(name='b')]
try:
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:
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):
self.backend.stored_playlists.playlists = [
Playlist(name='a'), Playlist(name='b')]
try:
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:
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):
original_playlist = self.stored.create(u'test')
original_playlist = self.stored.create('test')
looked_up_playlist = self.stored.lookup(original_playlist.uri)
@ -107,10 +109,10 @@ class StoredPlaylistsControllerTest(object):
pass
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)
playlist2 = playlist1.copy(name=u'test2')
playlist2 = playlist1.copy(name='test2')
playlist2 = self.stored.save(playlist2)
self.assertNotIn(playlist1, self.stored.playlists)
self.assertIn(playlist2, self.stored.playlists)

View File

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

View File

@ -1,6 +1,9 @@
from __future__ import unicode_literals
from mopidy.utils.path import path_to_uri
from tests import path_to_data_dir
song = path_to_data_dir('song%s.wav')
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.backends.local import LocalBackend
from mopidy.models import Track

View File

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

View File

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

View File

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

View File

@ -1,5 +1,7 @@
# encoding: utf-8
from __future__ import unicode_literals
import os
import tempfile
@ -12,7 +14,7 @@ from tests import unittest, path_to_data_dir
data_dir = path_to_data_dir('')
song1_path = path_to_data_dir('song1.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)
song2_uri = path_to_uri(song2_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(''))
uri = path_to_uri(path_to_data_dir('song1.mp3'))
artists = [Artist(name=u'æøå')]
album = Album(name=u'æøå', artists=artists)
artists = [Artist(name='æøå')]
album = Album(name='æøå', artists=artists)
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])

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import mock
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