From 5860a5dc2c8d5232603a02a31eee332bd879f636 Mon Sep 17 00:00:00 2001 From: Johan Brodin Date: Thu, 9 Jun 2016 09:42:51 +0200 Subject: [PATCH 1/4] Added market parameter to user_playlist_tracks According to https://developer.spotify.com/web-api/get-playlists-tracks/ this endpoints supports an market option. This is important to specify if you want to ensure 'preview_url' on your tracks (https://github.com/spotify/web-api/issues/148) --- spotipy/client.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spotipy/client.py b/spotipy/client.py index be9162b..c3b4b69 100644 --- a/spotipy/client.py +++ b/spotipy/client.py @@ -353,7 +353,7 @@ class Spotify(object): return self._get("users/%s/playlists/%s" % (user, plid), fields=fields) def user_playlist_tracks(self, user, playlist_id = None, fields=None, - limit=100, offset=0): + limit=100, offset=0, market=None): ''' Get full details of the tracks of a playlist owned by a user. Parameters: @@ -362,10 +362,11 @@ class Spotify(object): - fields - which fields to return - limit - the maximum number of tracks to return - offset - the index of the first track to return + - market - an ISO 3166-1 alpha-2 country code. ''' plid = self._get_id('playlist', playlist_id) return self._get("users/%s/playlists/%s/tracks" % (user, plid), - limit=limit, offset=offset, fields=fields) + limit=limit, offset=offset, fields=fields, market=market) def user_playlist_create(self, user, name, public=True): ''' Creates a playlist for a user From 848cbe5b9809037b5530c76e940b6f0b821dab17 Mon Sep 17 00:00:00 2001 From: Kevin M Granger Date: Wed, 15 Jun 2016 12:09:25 -0400 Subject: [PATCH 2/4] Make docs show __init__ docstrings --- docs/index.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/index.rst b/docs/index.rst index 34e7187..f9c3c48 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -252,6 +252,7 @@ API Reference .. automodule:: spotipy.client :members: :undoc-members: + :special-members: :show-inheritance: :mod:`oauth2` Module @@ -260,6 +261,7 @@ API Reference .. automodule:: spotipy.oauth2 :members: :undoc-members: + :special-members: :show-inheritance: :mod:`util` Module @@ -268,6 +270,7 @@ API Reference .. automodule:: spotipy.util :members: :undoc-members: + :special-members: :show-inheritance: From 8a26e0317844b0cb6e4d37b0b0bad70ef8844a94 Mon Sep 17 00:00:00 2001 From: Kevin M Granger Date: Thu, 16 Jun 2016 14:15:23 -0400 Subject: [PATCH 3/4] Add OAuth cache tests --- tests/test_oauth.py | 118 ++++++++++++++++++++++++++++++++++++++++++++ tox.ini | 4 +- 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 tests/test_oauth.py diff --git a/tests/test_oauth.py b/tests/test_oauth.py new file mode 100644 index 0000000..996f510 --- /dev/null +++ b/tests/test_oauth.py @@ -0,0 +1,118 @@ +from spotipy.oauth2 import SpotifyOAuth +import json +import io +import unittest + +try: + import unittest.mock as mock +except ImportError: + import mock + +patch = mock.patch +DEFAULT = mock.DEFAULT + + +def _make_fake_token(expires_at, expires_in, scope): + return dict( + expires_at=expires_at, + expires_in=expires_in, + scope=scope, + token_type="Bearer", + refresh_token="REFRESH", + access_token="ACCESS") + + +def _fake_file(): + return mock.Mock(spec_set=io.FileIO) + + +def _token_file(token): + fi = _fake_file() + fi.read.return_value = token + return fi + + +def _make_oauth(*args, **kwargs): + return SpotifyOAuth("CLID", "CLISEC", "REDIR", "STATE", *args, **kwargs) + + +class OAuthCacheTest(unittest.TestCase): + + @patch.multiple(SpotifyOAuth, + _is_token_expired=DEFAULT, _refresh_access_token=DEFAULT) + @patch('spotipy.oauth2.open', create=True) + def test_gets_from_cache_path(self, opener, + _is_token_expired, _refresh_access_token): + scope = "playlist-modify-private" + path = ".cache-username" + tok = _make_fake_token(1, 1, scope) + + opener.return_value = _token_file(json.dumps(tok, ensure_ascii=False)) + _is_token_expired.return_value = False + + spot = _make_oauth(scope, path) + cached_tok = spot.get_cached_token() + + opener.assert_called_with(path) + self.assertIsNotNone(cached_tok) + self.assertEqual(_refresh_access_token.call_count, 0) + + @patch.multiple(SpotifyOAuth, + _is_token_expired=DEFAULT, _refresh_access_token=DEFAULT) + @patch('spotipy.oauth2.open', create=True) + def test_expired_token_refreshes(self, opener, + _is_token_expired, _refresh_access_token): + scope = "playlist-modify-private" + path = ".cache-username" + expired_tok = _make_fake_token(0, None, scope) + fresh_tok = _make_fake_token(1, 1, scope) + + token_file = _token_file(json.dumps(expired_tok, ensure_ascii=False)) + opener.return_value = token_file + _refresh_access_token.return_value = fresh_tok + + spot = _make_oauth(scope, path) + spot.get_cached_token() + + _is_token_expired.assert_called_with(expired_tok) + _refresh_access_token.assert_called_with(expired_tok['refresh_token']) + opener.assert_any_call(path) + + @patch.multiple(SpotifyOAuth, + _is_token_expired=DEFAULT, _refresh_access_token=DEFAULT) + @patch('spotipy.oauth2.open', create=True) + def test_badly_scoped_token_bails(self, opener, + _is_token_expired, _refresh_access_token): + token_scope = "playlist-modify-public" + requested_scope = "playlist-modify-private" + path = ".cache-username" + tok = _make_fake_token(1, 1, token_scope) + + opener.return_value = _token_file(json.dumps(tok, ensure_ascii=False)) + _is_token_expired.return_value = False + + spot = _make_oauth(requested_scope, path) + cached_tok = spot.get_cached_token() + + opener.assert_called_with(path) + self.assertIsNone(cached_tok) + self.assertEqual(_refresh_access_token.call_count, 0) + + @patch('spotipy.oauth2.open', create=True) + def test_saves_to_cache_path(self, opener): + scope = "playlist-modify-private" + path = ".cache-username" + tok = _make_fake_token(1, 1, scope) + + fi = _fake_file() + opener.return_value = fi + + spot = SpotifyOAuth("CLID", "CLISEC", "REDIR", "STATE", scope, path) + spot._save_token_info(tok) + + opener.assert_called_with(path, 'w') + self.assertTrue(fi.write.called) + + +if __name__ == '__main__': + unittest.main() diff --git a/tox.ini b/tox.ini index ffce4a8..705b07e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,7 @@ [tox] envlist = py27,py34 [testenv] -deps=requests +deps= + requests + py27: mock commands=python -m unittest discover tests From c0cb4569997c8595091a436cae151b3ec7397c79 Mon Sep 17 00:00:00 2001 From: Kevin M Granger Date: Mon, 20 Jun 2016 10:30:15 -0400 Subject: [PATCH 4/4] Make special-members only show __init__ We don't need to see all of __dict__, thanks! --- docs/index.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index f9c3c48..415b706 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -252,7 +252,7 @@ API Reference .. automodule:: spotipy.client :members: :undoc-members: - :special-members: + :special-members: __init__ :show-inheritance: :mod:`oauth2` Module @@ -261,7 +261,7 @@ API Reference .. automodule:: spotipy.oauth2 :members: :undoc-members: - :special-members: + :special-members: __init__ :show-inheritance: :mod:`util` Module @@ -270,7 +270,7 @@ API Reference .. automodule:: spotipy.util :members: :undoc-members: - :special-members: + :special-members: __init__ :show-inheritance: