mirror of
https://github.com/spotipy-dev/spotipy.git
synced 2026-06-19 09:13:53 +00:00
Fallback on decoding error responses into string if decoding into JSON fails (#689)
* Fixed a bug in the initializers for the auth managers that produced a spurious warning message if you provide a cache handler and you set a value for the "SPOTIPY_CLIENT_USERNAME" environment variable. * fixed a bug in _get_auth_response_local_server which occurs if you provide a state parameter and click cancel in the authorization dialog * Fallback on decoding the error response body into text if decoding into JSON fails. * Fall back on decoding oauth errors into text if decoding into JSON fails. * Fixed unused `http_error` name in exeception clause. * Updated CHANGELOG Co-authored-by: Stéphane Bruckert <stephane.bruckert@gmail.com>
This commit is contained in:
parent
d0fc4425f7
commit
06551cd055
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Added
|
### Added
|
||||||
|
|
||||||
* Added `MemoryCacheHandler`, a cache handler that simply stores the token info in memory as an instance attribute of this class.
|
* Added `MemoryCacheHandler`, a cache handler that simply stores the token info in memory as an instance attribute of this class.
|
||||||
|
* If a network request returns an error status code but the response body cannot be decoded into JSON, then fall back on decoding the body into a string.
|
||||||
* Added `DjangoSessionCacheHandler`, a cache handler that stores the token in the session framework provided by Django. Web apps using spotipy with Django can directly use this for cache handling.
|
* Added `DjangoSessionCacheHandler`, a cache handler that stores the token in the session framework provided by Django. Web apps using spotipy with Django can directly use this for cache handling.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@ -247,16 +247,22 @@ class Spotify(object):
|
|||||||
except requests.exceptions.HTTPError as http_error:
|
except requests.exceptions.HTTPError as http_error:
|
||||||
response = http_error.response
|
response = http_error.response
|
||||||
try:
|
try:
|
||||||
msg = response.json()["error"]["message"]
|
json_response = response.json()
|
||||||
except (ValueError, KeyError):
|
error = json_response.get("error", {})
|
||||||
msg = "error"
|
msg = error.get("message")
|
||||||
try:
|
reason = error.get("reason")
|
||||||
reason = response.json()["error"]["reason"]
|
except ValueError:
|
||||||
except (ValueError, KeyError):
|
# if the response cannnot be decoded into JSON (which raises a ValueError),
|
||||||
|
# then try to decode it into text
|
||||||
|
|
||||||
|
# if we receive an empty string (which is falsy), then replace it with `None`
|
||||||
|
msg = response.text or None
|
||||||
reason = None
|
reason = None
|
||||||
|
|
||||||
logger.error('HTTP Error for %s to %s returned %s due to %s',
|
logger.error(
|
||||||
method, url, response.status_code, msg)
|
'HTTP Error for %s to %s with Params: %s returned %s due to %s',
|
||||||
|
method, url, args.get("params"), response.status_code, msg
|
||||||
|
)
|
||||||
|
|
||||||
raise SpotifyException(
|
raise SpotifyException(
|
||||||
response.status_code,
|
response.status_code,
|
||||||
|
|||||||
@ -129,6 +129,28 @@ class SpotifyAuthBase(object):
|
|||||||
)
|
)
|
||||||
return needle_scope <= haystack_scope
|
return needle_scope <= haystack_scope
|
||||||
|
|
||||||
|
def _handle_oauth_error(self, http_error):
|
||||||
|
response = http_error.response
|
||||||
|
try:
|
||||||
|
error_payload = response.json()
|
||||||
|
error = error_payload.get('error')
|
||||||
|
error_description = error_payload.get('error_description')
|
||||||
|
except ValueError:
|
||||||
|
# if the response cannnot be decoded into JSON (which raises a ValueError),
|
||||||
|
# then try do decode it into text
|
||||||
|
|
||||||
|
# if we receive an empty string (which is falsy), then replace it with `None`
|
||||||
|
error = response.txt or None
|
||||||
|
error_description = None
|
||||||
|
|
||||||
|
raise SpotifyOauthError(
|
||||||
|
'error: {0}, error_description: {1}'.format(
|
||||||
|
error, error_description
|
||||||
|
),
|
||||||
|
error=error,
|
||||||
|
error_description=error_description
|
||||||
|
)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
"""Make sure the connection (pool) gets closed"""
|
"""Make sure the connection (pool) gets closed"""
|
||||||
if isinstance(self._session, requests.Session):
|
if isinstance(self._session, requests.Session):
|
||||||
@ -231,23 +253,20 @@ class SpotifyClientCredentials(SpotifyAuthBase):
|
|||||||
self.OAUTH_TOKEN_URL, headers, payload
|
self.OAUTH_TOKEN_URL, headers, payload
|
||||||
)
|
)
|
||||||
|
|
||||||
response = self._session.post(
|
try:
|
||||||
self.OAUTH_TOKEN_URL,
|
response = self._session.post(
|
||||||
data=payload,
|
self.OAUTH_TOKEN_URL,
|
||||||
headers=headers,
|
data=payload,
|
||||||
verify=True,
|
headers=headers,
|
||||||
proxies=self.proxies,
|
verify=True,
|
||||||
timeout=self.requests_timeout,
|
proxies=self.proxies,
|
||||||
)
|
timeout=self.requests_timeout,
|
||||||
if response.status_code != 200:
|
)
|
||||||
error_payload = response.json()
|
response.raise_for_status()
|
||||||
raise SpotifyOauthError(
|
token_info = response.json()
|
||||||
'error: {0}, error_description: {1}'.format(
|
return token_info
|
||||||
error_payload['error'], error_payload['error_description']),
|
except requests.exceptions.HTTPError as http_error:
|
||||||
error=error_payload['error'],
|
self._handle_oauth_error(http_error)
|
||||||
error_description=error_payload['error_description'])
|
|
||||||
token_info = response.json()
|
|
||||||
return token_info
|
|
||||||
|
|
||||||
def _add_custom_values_to_token_info(self, token_info):
|
def _add_custom_values_to_token_info(self, token_info):
|
||||||
"""
|
"""
|
||||||
@ -439,13 +458,12 @@ class SpotifyOAuth(SpotifyAuthBase):
|
|||||||
self._open_auth_url()
|
self._open_auth_url()
|
||||||
server.handle_request()
|
server.handle_request()
|
||||||
|
|
||||||
if self.state is not None and server.state != self.state:
|
if server.error is not None:
|
||||||
raise SpotifyStateError(self.state, server.state)
|
|
||||||
|
|
||||||
if server.auth_code is not None:
|
|
||||||
return server.auth_code
|
|
||||||
elif server.error is not None:
|
|
||||||
raise server.error
|
raise server.error
|
||||||
|
elif self.state is not None and server.state != self.state:
|
||||||
|
raise SpotifyStateError(self.state, server.state)
|
||||||
|
elif server.auth_code is not None:
|
||||||
|
return server.auth_code
|
||||||
else:
|
else:
|
||||||
raise SpotifyOauthError("Server listening on localhost has not been accessed")
|
raise SpotifyOauthError("Server listening on localhost has not been accessed")
|
||||||
|
|
||||||
@ -529,25 +547,22 @@ class SpotifyOAuth(SpotifyAuthBase):
|
|||||||
self.OAUTH_TOKEN_URL, headers, payload
|
self.OAUTH_TOKEN_URL, headers, payload
|
||||||
)
|
)
|
||||||
|
|
||||||
response = self._session.post(
|
try:
|
||||||
self.OAUTH_TOKEN_URL,
|
response = self._session.post(
|
||||||
data=payload,
|
self.OAUTH_TOKEN_URL,
|
||||||
headers=headers,
|
data=payload,
|
||||||
verify=True,
|
headers=headers,
|
||||||
proxies=self.proxies,
|
verify=True,
|
||||||
timeout=self.requests_timeout,
|
proxies=self.proxies,
|
||||||
)
|
timeout=self.requests_timeout,
|
||||||
if response.status_code != 200:
|
)
|
||||||
error_payload = response.json()
|
response.raise_for_status()
|
||||||
raise SpotifyOauthError(
|
token_info = response.json()
|
||||||
'error: {0}, error_description: {1}'.format(
|
token_info = self._add_custom_values_to_token_info(token_info)
|
||||||
error_payload['error'], error_payload['error_description']),
|
self.cache_handler.save_token_to_cache(token_info)
|
||||||
error=error_payload['error'],
|
return token_info if as_dict else token_info["access_token"]
|
||||||
error_description=error_payload['error_description'])
|
except requests.exceptions.HTTPError as http_error:
|
||||||
token_info = response.json()
|
self._handle_oauth_error(http_error)
|
||||||
token_info = self._add_custom_values_to_token_info(token_info)
|
|
||||||
self.cache_handler.save_token_to_cache(token_info)
|
|
||||||
return token_info if as_dict else token_info["access_token"]
|
|
||||||
|
|
||||||
def refresh_access_token(self, refresh_token):
|
def refresh_access_token(self, refresh_token):
|
||||||
payload = {
|
payload = {
|
||||||
@ -562,28 +577,23 @@ class SpotifyOAuth(SpotifyAuthBase):
|
|||||||
self.OAUTH_TOKEN_URL, headers, payload
|
self.OAUTH_TOKEN_URL, headers, payload
|
||||||
)
|
)
|
||||||
|
|
||||||
response = self._session.post(
|
try:
|
||||||
self.OAUTH_TOKEN_URL,
|
response = self._session.post(
|
||||||
data=payload,
|
self.OAUTH_TOKEN_URL,
|
||||||
headers=headers,
|
data=payload,
|
||||||
proxies=self.proxies,
|
headers=headers,
|
||||||
timeout=self.requests_timeout,
|
proxies=self.proxies,
|
||||||
)
|
timeout=self.requests_timeout,
|
||||||
|
)
|
||||||
if response.status_code != 200:
|
response.raise_for_status()
|
||||||
error_payload = response.json()
|
token_info = response.json()
|
||||||
raise SpotifyOauthError(
|
token_info = self._add_custom_values_to_token_info(token_info)
|
||||||
'error: {0}, error_description: {1}'.format(
|
if "refresh_token" not in token_info:
|
||||||
error_payload['error'], error_payload['error_description']),
|
token_info["refresh_token"] = refresh_token
|
||||||
error=error_payload['error'],
|
self.cache_handler.save_token_to_cache(token_info)
|
||||||
error_description=error_payload['error_description'])
|
return token_info
|
||||||
|
except requests.exceptions.HTTPError as http_error:
|
||||||
token_info = response.json()
|
self._handle_oauth_error(http_error)
|
||||||
token_info = self._add_custom_values_to_token_info(token_info)
|
|
||||||
if "refresh_token" not in token_info:
|
|
||||||
token_info["refresh_token"] = refresh_token
|
|
||||||
self.cache_handler.save_token_to_cache(token_info)
|
|
||||||
return token_info
|
|
||||||
|
|
||||||
def _add_custom_values_to_token_info(self, token_info):
|
def _add_custom_values_to_token_info(self, token_info):
|
||||||
"""
|
"""
|
||||||
@ -901,26 +911,22 @@ class SpotifyPKCE(SpotifyAuthBase):
|
|||||||
self.OAUTH_TOKEN_URL, headers, payload
|
self.OAUTH_TOKEN_URL, headers, payload
|
||||||
)
|
)
|
||||||
|
|
||||||
response = self._session.post(
|
try:
|
||||||
self.OAUTH_TOKEN_URL,
|
response = self._session.post(
|
||||||
data=payload,
|
self.OAUTH_TOKEN_URL,
|
||||||
headers=headers,
|
data=payload,
|
||||||
verify=True,
|
headers=headers,
|
||||||
proxies=self.proxies,
|
verify=True,
|
||||||
timeout=self.requests_timeout,
|
proxies=self.proxies,
|
||||||
)
|
timeout=self.requests_timeout,
|
||||||
if response.status_code != 200:
|
)
|
||||||
error_payload = response.json()
|
response.raise_for_status()
|
||||||
raise SpotifyOauthError('error: {0}, error_descr: {1}'.format(error_payload['error'],
|
token_info = response.json()
|
||||||
error_payload[
|
token_info = self._add_custom_values_to_token_info(token_info)
|
||||||
'error_description'
|
self.cache_handler.save_token_to_cache(token_info)
|
||||||
]),
|
return token_info["access_token"]
|
||||||
error=error_payload['error'],
|
except requests.exceptions.HTTPError as http_error:
|
||||||
error_description=error_payload['error_description'])
|
self._handle_oauth_error(http_error)
|
||||||
token_info = response.json()
|
|
||||||
token_info = self._add_custom_values_to_token_info(token_info)
|
|
||||||
self.cache_handler.save_token_to_cache(token_info)
|
|
||||||
return token_info["access_token"]
|
|
||||||
|
|
||||||
def refresh_access_token(self, refresh_token):
|
def refresh_access_token(self, refresh_token):
|
||||||
payload = {
|
payload = {
|
||||||
@ -936,28 +942,23 @@ class SpotifyPKCE(SpotifyAuthBase):
|
|||||||
self.OAUTH_TOKEN_URL, headers, payload
|
self.OAUTH_TOKEN_URL, headers, payload
|
||||||
)
|
)
|
||||||
|
|
||||||
response = self._session.post(
|
try:
|
||||||
self.OAUTH_TOKEN_URL,
|
response = self._session.post(
|
||||||
data=payload,
|
self.OAUTH_TOKEN_URL,
|
||||||
headers=headers,
|
data=payload,
|
||||||
proxies=self.proxies,
|
headers=headers,
|
||||||
timeout=self.requests_timeout,
|
proxies=self.proxies,
|
||||||
)
|
timeout=self.requests_timeout,
|
||||||
|
)
|
||||||
if response.status_code != 200:
|
response.raise_for_status()
|
||||||
error_payload = response.json()
|
token_info = response.json()
|
||||||
raise SpotifyOauthError(
|
token_info = self._add_custom_values_to_token_info(token_info)
|
||||||
'error: {0}, error_description: {1}'.format(
|
if "refresh_token" not in token_info:
|
||||||
error_payload['error'], error_payload['error_description']),
|
token_info["refresh_token"] = refresh_token
|
||||||
error=error_payload['error'],
|
self.cache_handler.save_token_to_cache(token_info)
|
||||||
error_description=error_payload['error_description'])
|
return token_info
|
||||||
|
except requests.exceptions.HTTPError as http_error:
|
||||||
token_info = response.json()
|
self._handle_oauth_error(http_error)
|
||||||
token_info = self._add_custom_values_to_token_info(token_info)
|
|
||||||
if "refresh_token" not in token_info:
|
|
||||||
token_info["refresh_token"] = refresh_token
|
|
||||||
self.cache_handler.save_token_to_cache(token_info)
|
|
||||||
return token_info
|
|
||||||
|
|
||||||
def parse_response_code(self, url):
|
def parse_response_code(self, url):
|
||||||
""" Parse the response code in the given response url
|
""" Parse the response code in the given response url
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user