Skip to content
This repository has been archived by the owner on Jun 15, 2022. It is now read-only.

Commit

Permalink
Release v2.0.0b5
Browse files Browse the repository at this point in the history
  • Loading branch information
jodal committed May 8, 2015
2 parents 21f901c + 5c95488 commit 9c9b5d4
Show file tree
Hide file tree
Showing 50 changed files with 282 additions and 75 deletions.
1 change: 1 addition & 0 deletions .mailmap
@@ -1,3 +1,4 @@
Richard Ive <richard@xanox.net>
Thomas Vander Stichele <thomas@apestaart.org>
Nick Steel <kingosticks@gmail.com>
Tarik Dadi <daditarik@gmail.com>
20 changes: 16 additions & 4 deletions .travis.yml
@@ -1,5 +1,17 @@
sudo: false

language: python

addons:
apt:
sources:
- mopidy-stable
packages:
- libffi-dev
- libspotify-dev
- python-dev
- python3-dev

env:
- TOX_ENV=py27
- TOX_ENV=py32
Expand All @@ -11,10 +23,6 @@ env:
- TOX_ENV=flake8

install:
- "wget -O - http://apt.mopidy.com/mopidy.gpg | sudo apt-key add -"
- "sudo wget -O /etc/apt/sources.list.d/mopidy.list http://apt.mopidy.com/mopidy.list"
- "sudo apt-get update"
- "sudo apt-get install python-all-dev libffi-dev libspotify-dev"
- "pip install tox"

script:
Expand All @@ -23,6 +31,10 @@ script:
after_success:
- "if [ $TOX_ENV == 'py27' ]; then pip install coveralls; coveralls; fi"

branches:
except:
- v2.x/debian

notifications:
irc:
channels:
Expand Down
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -3,3 +3,4 @@
- Thomas Vander Stichele <thomas@apestaart.org>
- Nick Steel <kingosticks@gmail.com>
- Trygve Aaberge <trygveaa@gmail.com>
- Tarik Dadi <daditarik@gmail.com>
1 change: 1 addition & 0 deletions MANIFEST.in
@@ -1,5 +1,6 @@
include *.py
include *.rst
include *.txt
include .coveragerc
include .mailmap
include .travis.yml
Expand Down
6 changes: 6 additions & 0 deletions dev-requirements.txt
@@ -0,0 +1,6 @@
flake8
flake8-import-order
mock
pytest
pytest-cov
tox
2 changes: 2 additions & 0 deletions docs/api/playlist.rst
Expand Up @@ -33,6 +33,8 @@ Playlists
.. autoclass:: PlaylistOfflineStatus
:no-inherited-members:

.. autoclass:: PlaylistPlaceholder

.. autoclass:: PlaylistTrack

.. autoclass:: PlaylistType
Expand Down
28 changes: 28 additions & 0 deletions docs/changelog.rst
Expand Up @@ -2,6 +2,34 @@
Changelog
*********

v2.0.0b5 (2015-05-09)
=====================

A fifth beta with a couple of bug fixes.

Minor changes
-------------

- Changed :meth:`spotify.Link.as_playlist()` to also support creating playlists
from links with type :attr:`spotify.LinkType.STARRED`.

- Changed all ``load()`` methods to raise :exc:`spotify.Error` instead of
:exc:`RuntimeError` if the session isn't logged in.

- Changed from nose to py.test as test runner.

Bug fixes
---------

- Work around segfault in libspotify when :attr:`spotify.Config.cache_location`
is set to :class:`None` and then used to create a session. (Fixes:
:issue:`151`)

- Return a :class:`spotify.PlaylistPlaceholder` object instead of raising an
exception if the playlist container contains an element of type
:attr:`~spotify.PlaylistType.PLACEHOLDER`. (Fixes: :issue:`159`)


v2.0.0b4 (2015-01-13)
=====================

Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Expand Up @@ -47,7 +47,7 @@ def get_version(filename):


# Unwrap decorated methods so Sphinx can inspect their signatures
import spotify
import spotify # flake8: noqa
for mod_name, mod in vars(spotify).items():
if not isinstance(mod, types.ModuleType) or mod_name in ('threading',):
continue
Expand Down
9 changes: 2 additions & 7 deletions docs/contributing.rst
Expand Up @@ -38,18 +38,13 @@ Development setup

4. Install development dependencies::

pip install cffi nose tox

Note that if you're developing on Python <3.3, you also need to install
``mock``::

pip install mock
pip install -r dev-requirements.txt

5. Run tests.

For a quick test suite run, using the virtualenv's Python version::

nosetests
py.test

For a complete test suite run, using all the Python implementations::

Expand Down
2 changes: 1 addition & 1 deletion docs/installation.rst
Expand Up @@ -71,7 +71,7 @@ libspotify in the homebrew/binary tap::
A known workaround is to create a symlink after installing libspotify, but
before installing pyspotify::

sudo ln -s /usr/local/opt/libspotify/lib/libspotify.12.1.51.dylib \
ln -s /usr/local/opt/libspotify/lib/libspotify.12.1.51.dylib \
/usr/local/opt/libspotify/lib/libspotify

Alternatively, the mopidy/mopidy Homebrew tap has a libspotify formula which
Expand Down
23 changes: 9 additions & 14 deletions docs/plans.rst
Expand Up @@ -7,27 +7,22 @@ pyspotify 2.x is a new Python wrapper for `libspotify
still under active development.


v2.0.0b5: Seen real usage
=========================

- Reimplement Mopidy-Spotify using pyspotify 2. Will surely lead to bug fixes
and/or API changes.
v2.0.0: Final release
=====================

- Look into how asyncio support could work.
- Fix all remaining TODOs in code and tests.

- Maybe add some more features to the jukebox example.
- Fix all remaining FIXMEs in code and tests.

- Iterate a few times over the docs to improve them as much as possible.
- Revisit all XXXs in code and tests.


v2.0.0: Final release
=====================
Future plans
============

- Enforce that the same cache and settings directories are not used by multiple
processes by maintaining a lock file.

- Fix all remaining TODOs in code and tests.

- Fix all remaining FIXMEs in code and tests.
- Iterate a few times over the docs to improve them as much as possible.

- Revisit all XXXs in code and tests.
- Look into how asyncio support could work.
1 change: 1 addition & 0 deletions docs/requirements.txt
@@ -1 +1,2 @@
mock >= 1.0
Sphinx >= 1.0
12 changes: 9 additions & 3 deletions examples/shell.py
Expand Up @@ -99,7 +99,11 @@ def do_EOF(self, line):

def do_login(self, line):
"login <username> <password>"
username, password = line.split(' ', 1)
tokens = line.split(' ', 1)
if len(tokens) != 2:
self.logger.warning("Wrong number of arguments")
return
username, password = tokens
self.session.login(username, password, remember_me=True)
self.logged_in.wait()

Expand Down Expand Up @@ -165,9 +169,11 @@ def do_stop(self, line):
def do_seek(self, seconds):
"seek <seconds>"
if not self.logged_in.is_set():
self.logger.warning('You must be logged in to play')
self.logger.warning('You must be logged in to seek')
return
if self.session.player.state is spotify.PlayerState.UNLOADED:
self.logger.warning('A track must be loaded before seeking')
return
# TODO Check if playing
self.session.player.seek(int(seconds) * 1000)

def do_search(self, query):
Expand Down
9 changes: 3 additions & 6 deletions setup.py
Expand Up @@ -4,7 +4,7 @@

from distutils.command.build import build

from setuptools import setup, find_packages
from setuptools import find_packages, setup
from setuptools.command.install import install


Expand All @@ -20,13 +20,15 @@ def get_version(filename):


class CFFIBuild(build):

def finalize_options(self):
from spotify import ffi
self.distribution.ext_modules = [ffi.verifier.get_extension()]
build.finalize_options(self)


class CFFIInstall(install):

def finalize_options(self):
from spotify import ffi
self.distribution.ext_modules = [ffi.verifier.get_extension()]
Expand Down Expand Up @@ -56,11 +58,6 @@ def finalize_options(self):
'build': CFFIBuild,
'install': CFFIInstall,
},
test_suite='nose.collector',
tests_require=[
'nose',
'mock >= 1.0',
],
classifiers=[
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
Expand Down
20 changes: 12 additions & 8 deletions spotify/__init__.py
Expand Up @@ -5,7 +5,7 @@
import threading


__version__ = '2.0.0b4'
__version__ = '2.0.0b5'


# Global reentrant lock to be held whenever libspotify functions are called or
Expand Down Expand Up @@ -64,14 +64,18 @@ def wrapper(*args, **kwargs):
return wrapper


def _serialize_access_to_library(lib):
"""Modify CFFI library to serialize all calls to library functions.
class _SerializedLib(object):
"""CFFI library wrapper to serialize all calls to library functions.
Internal function.
Internal class.
"""
for name in dir(lib):
if name.startswith('sp_') and callable(getattr(lib, name)):
setattr(lib, name, serialized(getattr(lib, name)))

def __init__(self, lib):
for name in dir(lib):
attr = getattr(lib, name)
if name.startswith('sp_') and callable(attr):
attr = serialized(attr)
setattr(self, name, attr)


def _get_cffi_modulename(header, source, sys_version):
Expand Down Expand Up @@ -115,7 +119,7 @@ def _build_ffi():
libraries=[str('spotify')],
ext_package='spotify')

_serialize_access_to_library(lib)
lib = _SerializedLib(lib)

return ffi, lib

Expand Down
2 changes: 2 additions & 0 deletions spotify/album.py
Expand Up @@ -17,6 +17,7 @@


class Album(object):

"""A Spotify album.
You can get an album from a track or an artist, or you can create an
Expand Down Expand Up @@ -187,6 +188,7 @@ def browse(self, callback=None):


class AlbumBrowser(object):

"""An album browser for a Spotify album.
You can get an album browser from any :class:`Album` instance by calling
Expand Down
2 changes: 2 additions & 0 deletions spotify/artist.py
Expand Up @@ -17,6 +17,7 @@


class Artist(object):

"""A Spotify artist.
You can get artists from tracks and albums, or you can create an
Expand Down Expand Up @@ -149,6 +150,7 @@ def browse(self, type=None, callback=None):


class ArtistBrowser(object):

"""An artist browser for a Spotify artist.
You can get an artist browser from any :class:`Artist` instance by calling
Expand Down
2 changes: 2 additions & 0 deletions spotify/audio.py
Expand Up @@ -15,6 +15,7 @@

class AudioBufferStats(collections.namedtuple(
'AudioBufferStats', ['samples', 'stutter'])):

"""Stats about the application's audio buffers."""
pass

Expand All @@ -30,6 +31,7 @@ class SampleType(utils.IntEnum):


class AudioFormat(object):

"""A Spotify audio format object.
You'll never need to create an instance of this class yourself, but you'll
Expand Down
7 changes: 6 additions & 1 deletion spotify/config.py
Expand Up @@ -10,6 +10,7 @@


class Config(object):

"""The session config.
Create an instance and assign to its attributes to configure. Then use the
Expand Down Expand Up @@ -63,7 +64,11 @@ def cache_location(self):

@cache_location.setter
def cache_location(self, value):
self._cache_location = utils.to_char_or_null(value)
# XXX: libspotify segfaults if cache_location is set to NULL, but
# doesn't seem to care if other strings in sp_session_config is NULL.
if value is None:
value = ''
self._cache_location = utils.to_char(value)
self._sp_session_config.cache_location = self._cache_location

@property
Expand Down
1 change: 1 addition & 0 deletions spotify/connection.py
Expand Up @@ -15,6 +15,7 @@


class Connection(object):

"""Connection controller.
You'll never need to create an instance of this class yourself. You'll find
Expand Down
3 changes: 3 additions & 0 deletions spotify/error.py
Expand Up @@ -12,6 +12,7 @@


class Error(Exception):

"""A Spotify error.
This is the superclass of all custom exceptions raised by pyspotify.
Expand All @@ -36,6 +37,7 @@ class ErrorType(utils.IntEnum):


class LibError(Error):

"""A libspotify error.
Where many libspotify functions return error codes that must be checked
Expand Down Expand Up @@ -68,6 +70,7 @@ def __ne__(self, other):


class Timeout(Error):

"""Exception raised by an operation not completing within the given
timeout."""

Expand Down

0 comments on commit 9c9b5d4

Please sign in to comment.