Merge branch 'release/v0.19.x' into develop
This commit is contained in:
commit
c50641230d
@ -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)
|
||||
====================
|
||||
|
||||
@ -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.*
|
||||
"""
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user