diff --git a/CHANGELOG.md b/CHANGELOG.md
index ba4a44b..30e8930 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,13 +6,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Added
+
+- Added CONTRIBUTING.md
+
### Changed
+
- Client retry logic has changed as it now uses urllib3's `Retry` in conjunction with requests `Session`
- The session is customizable as it allows for:
- status_forcelist
- retries
- status_retries
- backoff_factor
+- Spin up a local webserver to auto-fill authentication URL
## [2.10.0] - 2020-03-18
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1b94f52..c40d756 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,7 +1,9 @@
## Contributing
+
If you would like to contribute to spotipy follow these steps:
### Export the needed environment variables
+
```bash
export SPOTIPY_CLIENT_ID=client_id_here
export SPOTIPY_CLIENT_SECRET=client_secret_here
@@ -9,10 +11,26 @@ export SPOTIPY_CLIENT_USERNAME=client_username_here # This is actually an id not
export SPOTIPY_REDIRECT_URI=http://localhost/ # Make url is set in app you created to get your ID and SECRET
```
-### Create virtual enevironment, install dependencies, run tests:
+### Create virtual environment, install dependencies, run tests:
+
```bash
$ virtualenv --python=python3.7 env
(env) $ pip install requirements.txt
(env) $ python -m unittest discover -v tests
```
+### Lint
+
+To automatically fix the code style:
+
+ pip install autopep8
+ autopep8 --in-place --aggressive --recursive .
+
+To verify the code style:
+
+ pip install flake8
+ flake8 .
+
+### README
+
+Don't forget to add a short description of your change in the [CHANGELOG](CHANGELOG.md)
\ No newline at end of file
diff --git a/spotipy/oauth2.py b/spotipy/oauth2.py
index b8b2544..483271f 100644
--- a/spotipy/oauth2.py
+++ b/spotipy/oauth2.py
@@ -320,10 +320,9 @@ class SpotifyOAuth(SpotifyAuthBase):
return self.parse_response_code(response)
def _get_auth_response_local_server(self, redirect_port):
-
- with start_local_http_server(redirect_port) as server:
- self._open_auth_url()
- server.handle_request()
+ server = start_local_http_server(redirect_port)
+ self._open_auth_url()
+ server.handle_request()
if server.auth_code is not None:
return server.auth_code
@@ -488,9 +487,12 @@ class RequestHandler(BaseHTTPRequestHandler):
return
self._write("""
+
Authentication status: {}
-Now you can close this window or tab.
+This window can be closed.
""".format(status))
diff --git a/tests/integration/test_non_user_endpoints.py b/tests/integration/test_non_user_endpoints.py
index fdb5886..1c17d2c 100644
--- a/tests/integration/test_non_user_endpoints.py
+++ b/tests/integration/test_non_user_endpoints.py
@@ -192,8 +192,9 @@ class AuthTestSpotipy(unittest.TestCase):
sp = spotipy.Spotify(requests_timeout=0.01,
client_credentials_manager=client_credentials_manager)
- with self.assertRaises(requests.exceptions.Timeout):
- sp.search(q='my*', type='track')
+ # depending on the timing or bandwidth, this raises a timeout or connection error"
+ self.assertRaises((requests.exceptions.Timeout, requests.exceptions.ConnectionError),
+ lambda: sp.search(q='my*', type='track'))
def test_album_search(self):
results = self.spotify.search(q='weezer pinkerton', type='album')
diff --git a/tests/integration/test_user_endpoints.py b/tests/integration/test_user_endpoints.py
index 1705f6d..adf0d6d 100644
--- a/tests/integration/test_user_endpoints.py
+++ b/tests/integration/test_user_endpoints.py
@@ -59,7 +59,7 @@ class SpotipyPlaylistApiTest(unittest.TestCase):
user = playlist['owner']['id']
pid = playlist['id']
results = self.spotify.user_playlist_tracks(user, pid)
- self.assertEquals(len(results['items']), 0)
+ self.assertEqual(len(results['items']), 0)
def test_current_user_playlists(self):
playlists = self.spotify.current_user_playlists(limit=10)
@@ -134,7 +134,7 @@ class SpotipyPlaylistApiTest(unittest.TestCase):
self.spotify.playlist_upload_cover_image(self.new_playlist_uri, dog_base64)
res = self.spotify.playlist_cover_image(self.new_playlist_uri)
- self.assertEquals(len(res), 1)
+ self.assertEqual(len(res), 1)
first_image = res[0]
self.assertIn('width', first_image)
self.assertIn('height', first_image)
@@ -196,13 +196,13 @@ class SpotipyLibraryApiTests(unittest.TestCase):
tracks = self.spotify.current_user_saved_tracks()
new_total = tracks['total']
- self.assertEquals(new_total - total, len(self.four_tracks))
+ self.assertEqual(new_total - total, len(self.four_tracks))
tracks = self.spotify.current_user_saved_tracks_delete(
self.four_tracks)
tracks = self.spotify.current_user_saved_tracks()
new_total = tracks['total']
- self.assertEquals(new_total, total)
+ self.assertEqual(new_total, total)
def test_current_user_saved_albums(self):
# Add
@@ -212,12 +212,12 @@ class SpotipyLibraryApiTests(unittest.TestCase):
# Contains
resp = self.spotify.current_user_saved_albums_contains(self.album_ids)
- self.assertEquals(resp, [True, True])
+ self.assertEqual(resp, [True, True])
# Remove
self.spotify.current_user_saved_albums_delete(self.album_ids)
resp = self.spotify.current_user_saved_albums_contains(self.album_ids)
- self.assertEquals(resp, [False, False])
+ self.assertEqual(resp, [False, False])
class SpotipyUserApiTests(unittest.TestCase):
@@ -244,11 +244,11 @@ class SpotipyUserApiTests(unittest.TestCase):
def test_basic_user_profile(self):
user = self.spotify.user(self.username)
- self.assertEquals(user['id'], self.username.lower())
+ self.assertEqual(user['id'], self.username.lower())
def test_current_user(self):
user = self.spotify.current_user()
- self.assertEquals(user['id'], self.username.lower())
+ self.assertEqual(user['id'], self.username.lower())
def test_me(self):
user = self.spotify.me()
@@ -373,7 +373,7 @@ class SpotipyPlayerApiTests(unittest.TestCase):
def test_devices(self):
# No devices playing by default
res = self.spotify.devices()
- self.assertEquals(len(res["devices"]), 0)
+ self.assertEqual(len(res["devices"]), 0)
def test_current_user_recently_played(self):
# No cursor