diff --git a/CHANGELOG.md b/CHANGELOG.md index 6769291..e602185 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Add your changes below. - Updated TUTORIAL.md instructions to match current layout of Spotify Developer Dashboard - Added test_artist_id, test_artist_url, and test_artists_mixed_ids to non_user_endpoints test.py - Added rate/request limit to FAQ +- Added custom `urllib3.Retry` class for printing a warning when a rate/request limit is reached. ### Fixed - Audiobook integration tests diff --git a/spotipy/client.py b/spotipy/client.py index f53c79a..caaf9ca 100644 --- a/spotipy/client.py +++ b/spotipy/client.py @@ -8,9 +8,9 @@ import re import warnings import requests -import urllib3 from spotipy.exceptions import SpotifyException +from spotipy.util import Retry from collections import defaultdict @@ -220,7 +220,7 @@ class Spotify: def _build_session(self): self._session = requests.Session() - retry = urllib3.Retry( + retry = Retry( total=self.retries, connect=None, read=False, diff --git a/spotipy/util.py b/spotipy/util.py index 1c91d74..2d9e012 100644 --- a/spotipy/util.py +++ b/spotipy/util.py @@ -1,3 +1,5 @@ +from __future__ import annotations + """ Shows a user's playlists. This needs to be authenticated via OAuth. """ __all__ = ["CLIENT_CREDS_ENV_VARS", "prompt_for_user_token"] @@ -5,9 +7,12 @@ __all__ = ["CLIENT_CREDS_ENV_VARS", "prompt_for_user_token"] import logging import os import warnings +from types import TracebackType import spotipy +import urllib3 + LOGGER = logging.getLogger(__name__) CLIENT_CREDS_ENV_VARS = { @@ -142,3 +147,29 @@ def normalize_scope(scope): return " ".join(sorted(scopes)) else: return None + + +class Retry(urllib3.Retry): + """ + Custom class for printing a warning when a rate/request limit is reached. + """ + def increment( + self, + method: str | None = None, + url: str | None = None, + response: urllib3.BaseHTTPResponse | None = None, + error: Exception | None = None, + _pool: urllib3.connectionpool.ConnectionPool | None = None, + _stacktrace: TracebackType | None = None, + ) -> urllib3.Retry: + if response: + retry_header = response.headers.get("Retry-After") + if self.is_retry(method, response.status, bool(retry_header)): + logging.warning("Your application has reached a rate/request limit. " + f"Retry will occur after: {retry_header}") + return super().increment(method, + url, + response=response, + error=error, + _pool=_pool, + _stacktrace=_stacktrace)