Merge branch 'release/v0.19.x' into develop

This commit is contained in:
Stein Magnus Jodal 2014-08-29 14:03:11 +02:00
commit c50641230d
7 changed files with 43 additions and 18 deletions

View File

@ -35,6 +35,17 @@ Bug fix release.
- Configuration: :option:`mopidy --config` now supports directories.
- Network: Fix a race condition where two threads could try to free the same
data simultaneously. (Fixes: :issue:`781`)
- Backend API: Update :meth:`mopidy.backend.LibraryProvider.browse` signature
and docs to match how the core use the backend's browse method. (Fixes:
:issue:`833`)
- Local library API: Add :attr:`mopidy.local.ROOT_DIRECTORY_URI` constant for
use by implementors of :method:`mopidy.local.Library.browse`. (Related to:
:issue:`833`)
v0.19.3 (2014-08-03)
====================

View File

@ -81,12 +81,12 @@ class LibraryProvider(object):
def __init__(self, backend):
self.backend = backend
def browse(self, path):
def browse(self, uri):
"""
See :meth:`mopidy.core.LibraryController.browse`.
If you implement this method, make sure to also set
:attr:`root_directory_name`.
:attr:`root_directory`.
*MAY be implemented by subclass.*
"""

View File

@ -46,6 +46,15 @@ class Extension(ext.Extension):
return LocalCommand()
ROOT_DIRECTORY_URI = 'local:directory'
"""
URI of the local backend's root directory.
This constant should be used by libraries implementing the
:meth:`Library.browse` method.
"""
class Library(object):
"""
Local library interface.
@ -64,11 +73,14 @@ class Library(object):
def __init__(self, config):
self._config = config
def browse(self, path):
def browse(self, uri):
"""
Browse directories and tracks at the given path.
Browse directories and tracks at the given URI.
:param string path: path to browse or None for root.
The URI for the root directory is a constant available at
:attr:`ROOT_DIRECTORY_URI`.
:param string path: URI to browse.
:rtype: List of :class:`~mopidy.models.Ref` tracks and directories.
"""
raise NotImplementedError

View File

@ -61,8 +61,7 @@ class _BrowseCache(object):
splitpath_re = re.compile(r'([^/]+)')
def __init__(self, uris):
# TODO: local.ROOT_DIRECTORY_URI
self._cache = {'local:directory': collections.OrderedDict()}
self._cache = {local.ROOT_DIRECTORY_URI: collections.OrderedDict()}
for track_uri in uris:
path = translator.local_track_uri_to_path(track_uri, b'/')
@ -97,10 +96,10 @@ class _BrowseCache(object):
else:
# Loop completed, so final child needs to be added to root.
if child:
self._cache['local:directory'][child.uri] = child
self._cache[local.ROOT_DIRECTORY_URI][child.uri] = child
# If no parent was set we belong in the root.
if not parent_uri:
parent_uri = 'local:directory'
parent_uri = local.ROOT_DIRECTORY_URI
self._cache[parent_uri][track_uri] = track_ref

View File

@ -2,7 +2,7 @@ from __future__ import unicode_literals
import logging
from mopidy import backend, models
from mopidy import backend, local, models
logger = logging.getLogger(__name__)
@ -10,18 +10,18 @@ logger = logging.getLogger(__name__)
class LocalLibraryProvider(backend.LibraryProvider):
"""Proxy library that delegates work to our active local library."""
root_directory = models.Ref.directory(uri=b'local:directory',
name='Local media')
root_directory = models.Ref.directory(
uri=local.ROOT_DIRECTORY_URI, name='Local media')
def __init__(self, backend, library):
super(LocalLibraryProvider, self).__init__(backend)
self._library = library
self.refresh()
def browse(self, path):
def browse(self, uri):
if not self._library:
return []
return self._library.browse(path)
return self._library.browse(uri)
def refresh(self, uri=None):
if not self._library:

View File

@ -268,8 +268,8 @@ class Connection(object):
return True
if not data:
self.actor_ref.tell({'close': True})
self.disable_recv()
self.actor_ref.tell({'close': True})
return True
try:

View File

@ -7,7 +7,7 @@ import unittest
import gobject
from mock import Mock, patch, sentinel
from mock import Mock, call, patch, sentinel
import pykka
@ -418,8 +418,11 @@ class ConnectionTest(unittest.TestCase):
self.assertTrue(network.Connection.recv_callback(
self.mock, sentinel.fd, gobject.IO_IN))
self.mock.actor_ref.tell.assert_called_once_with({'close': True})
self.mock.disable_recv.assert_called_once_with()
self.assertEqual(self.mock.mock_calls, [
call.sock.recv(any_int),
call.disable_recv(),
call.actor_ref.tell({'close': True}),
])
def test_recv_callback_recoverable_error(self):
self.mock.sock = Mock(spec=socket.SocketType)