From 94b63059190a00ad046462339b746465d838d18d Mon Sep 17 00:00:00 2001 From: bvandercar-vt Date: Fri, 23 Jan 2026 11:06:52 -0700 Subject: [PATCH] style: use pipe notation, change minimum version to 3.10 --- .github/workflows/integration_tests.yml | 2 +- .github/workflows/unit_tests.yml | 2 +- setup.py | 2 +- spotipy/cache_handler.py | 14 +- spotipy/client.py | 178 ++++++++++++------------ spotipy/oauth2.py | 112 +++++++-------- spotipy/util.py | 32 ++--- 7 files changed, 171 insertions(+), 171 deletions(-) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 4d7d370..ba662bf 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -10,7 +10,7 @@ jobs: SPOTIPY_CLIENT_SECRET: ${{ secrets.SPOTIPY_CLIENT_SECRET }} strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index b472930..91e5935 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} diff --git a/setup.py b/setup.py index 5b36cdc..6504db0 100644 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ setup( project_urls={ 'Source': 'https://github.com/plamere/spotipy', }, - python_requires='>3.8', + python_requires='>=3.10', install_requires=[ "redis>=3.5.3", # TODO: Move to extras_require in v3 "requests>=2.25.0", diff --git a/spotipy/cache_handler.py b/spotipy/cache_handler.py index 8c71dee..ee8bc15 100644 --- a/spotipy/cache_handler.py +++ b/spotipy/cache_handler.py @@ -12,7 +12,7 @@ import json import logging import os from json import JSONEncoder -from typing import Dict, Optional +from typing import Dict from redis import RedisError @@ -53,9 +53,9 @@ class CacheFileHandler(CacheHandler): def __init__( self, - cache_path: Optional[str] = None, - username: Optional[str] = None, - encoder_cls: Optional[JSONEncoder] = None, + cache_path: str | None = None, + username: str | None = None, + encoder_cls: JSONEncoder | None = None, ): """ Parameters: @@ -113,7 +113,7 @@ class MemoryCacheHandler(CacheHandler): instance is freed. """ - def __init__(self, token_info: Optional[Dict] = None): + def __init__(self, token_info: Dict | None = None): """ Parameters: * token_info: The token info to store in memory. Can be None. @@ -189,7 +189,7 @@ class RedisCacheHandler(CacheHandler): A cache handler that stores the token info in the Redis. """ - def __init__(self, redis, key: Optional[str] = None): + def __init__(self, redis, key: str | None = None): """ Parameters: * redis: Redis object provided by redis-py library @@ -222,7 +222,7 @@ class MemcacheCacheHandler(CacheHandler): """A Cache handler that stores the token info in Memcache using the pymemcache client """ - def __init__(self, memcache, key: Optional[str] = None): + def __init__(self, memcache, key: str | None = None): """ Parameters: * memcache: memcache client object provided by pymemcache diff --git a/spotipy/client.py b/spotipy/client.py index f439e80..5e91a95 100644 --- a/spotipy/client.py +++ b/spotipy/client.py @@ -7,7 +7,7 @@ import logging import re import warnings from collections import defaultdict -from typing import Dict, List, Optional, TypedDict, Union +from typing import Dict, List, TypedDict, Union import requests @@ -138,7 +138,7 @@ class Spotify: retries: int = max_retries, status_retries: int = max_retries, backoff_factor: float = 0.3, - language: Optional[str] = None, + language: str | None = None, ): """ Creates a Spotify API client. @@ -360,7 +360,7 @@ class Spotify: else: return None - def track(self, track_id: str, market: Optional[str] = None): + def track(self, track_id: str, market: str | None = None): """ returns a single track given the track's ID, URI or URL Parameters: @@ -371,7 +371,7 @@ class Spotify: trid = self._get_id("track", track_id) return self._get("tracks/" + trid, market=market) - def tracks(self, tracks: StrListOrTuple, market: Optional[str] = None): + def tracks(self, tracks: StrListOrTuple, market: str | None = None): """ returns a list of tracks given a list of track IDs, URIs, or URLs Parameters: @@ -405,9 +405,9 @@ class Spotify: def artist_albums( self, artist_id: str, - album_type: Optional[str] = None, - include_groups: Optional[str] = None, - country: Optional[str] = None, + album_type: str | None = None, + include_groups: str | None = None, + country: str | None = None, limit: int = 20, offset: int = 0, ): @@ -476,7 +476,7 @@ class Spotify: trid = self._get_id("artist", artist_id) return self._get("artists/" + trid + "/related-artists") - def album(self, album_id: str, market: Optional[str] = None): + def album(self, album_id: str, market: str | None = None): """ returns a single album given the album's ID, URIs or URL Parameters: @@ -495,7 +495,7 @@ class Spotify: album_id: str, limit: int = 50, offset: int = 0, - market: Optional[str] = None, + market: str | None = None, ): """ Get Spotify catalog information about an album's tracks @@ -512,7 +512,7 @@ class Spotify: "albums/" + trid + "/tracks/", limit=limit, offset=offset, market=market ) - def albums(self, albums: StrListOrTuple, market: Optional[str] = None): + def albums(self, albums: StrListOrTuple, market: str | None = None): """ returns a list of albums given the album IDs, URIs, or URLs Parameters: @@ -526,7 +526,7 @@ class Spotify: else: return self._get("albums/?ids=" + ",".join(tlist)) - def show(self, show_id: str, market: Optional[str] = None): + def show(self, show_id: str, market: str | None = None): """ returns a single show given the show's ID, URIs or URL Parameters: @@ -541,7 +541,7 @@ class Spotify: trid = self._get_id("show", show_id) return self._get("shows/" + trid, market=market) - def shows(self, shows: StrListOrTuple, market: Optional[str] = None): + def shows(self, shows: StrListOrTuple, market: str | None = None): """ returns a list of shows given the show IDs, URIs, or URLs Parameters: @@ -561,7 +561,7 @@ class Spotify: show_id: str, limit: int = 50, offset: int = 0, - market: Optional[str] = None, + market: str | None = None, ): """ Get Spotify catalog information about a show's episodes @@ -581,7 +581,7 @@ class Spotify: "shows/" + trid + "/episodes/", limit=limit, offset=offset, market=market ) - def episode(self, episode_id: str, market: Optional[str] = None): + def episode(self, episode_id: str, market: str | None = None): """ returns a single episode given the episode's ID, URIs or URL Parameters: @@ -596,7 +596,7 @@ class Spotify: trid = self._get_id("episode", episode_id) return self._get("episodes/" + trid, market=market) - def episodes(self, episodes: StrListOrTuple, market: Optional[str] = None): + def episodes(self, episodes: StrListOrTuple, market: str | None = None): """ returns a list of episodes given the episode IDs, URIs, or URLs Parameters: @@ -617,7 +617,7 @@ class Spotify: limit: int = 10, offset: int = 0, type: str = "track", - market: Optional[str] = None, + market: str | None = None, ): """ searches for an item @@ -643,8 +643,8 @@ class Spotify: limit: int = 10, offset: int = 0, type: str = "track", - markets: Optional[StrListOrTuple] = None, - total: Optional[int] = None, + markets: StrListOrTuple | None = None, + total: int | None = None, ): """ (experimental) Searches multiple markets for an item @@ -696,8 +696,8 @@ class Spotify: def playlist( self, playlist_id: str, - fields: Optional[str] = None, - market: Optional[str] = None, + fields: str | None = None, + market: str | None = None, additional_types: StrListOrTuple = ("track",), ): """ Gets playlist by id. @@ -721,10 +721,10 @@ class Spotify: def playlist_tracks( self, playlist_id: str, - fields: Optional[str] = None, + fields: str | None = None, limit: int = 100, offset: int = 0, - market: Optional[str] = None, + market: str | None = None, additional_types: StrListOrTuple = ("track",) ): """ Get full details of the tracks of a playlist. @@ -753,10 +753,10 @@ class Spotify: def playlist_items( self, playlist_id: str, - fields: Optional[str] = None, + fields: str | None = None, limit: int = 100, offset: int = 0, - market: Optional[str] = None, + market: str | None = None, additional_types: StrListOrTuple = ("track", "episode") ): """ Get full details of the tracks and episodes of a playlist. @@ -807,9 +807,9 @@ class Spotify: def user_playlist( self, user: str, - playlist_id: Optional[str] = None, - fields: Optional[str] = None, - market: Optional[str] = None, + playlist_id: str | None = None, + fields: str | None = None, + market: str | None = None, ): """ Gets a single playlist of a user @@ -833,12 +833,12 @@ class Spotify: def user_playlist_tracks( self, - user: Optional[str] = None, - playlist_id: Optional[str] = None, - fields: Optional[str] = None, + user: str | None = None, + playlist_id: str | None = None, + fields: str | None = None, limit: int = 100, offset: int = 0, - market: Optional[str] = None, + market: str | None = None, ): """ Get full details of the tracks of a playlist owned by a user. @@ -908,10 +908,10 @@ class Spotify: self, user: str, playlist_id: str, - name: Optional[str] = None, - public: Optional[bool] = None, - collaborative: Optional[bool] = None, - description: Optional[str] = None, + name: str | None = None, + public: bool | None = None, + collaborative: bool | None = None, + description: str | None = None, ): """ This function is no longer in use, please use the recommended function in the warning! @@ -961,7 +961,7 @@ class Spotify: user: str, playlist_id: str, tracks: StrListOrTuple, - position: Optional[int] = None, + position: int | None = None, ): """ This function is no longer in use, please use the recommended function in the warning! @@ -990,7 +990,7 @@ class Spotify: user: str, playlist_id: str, episodes: StrListOrTuple, - position: Optional[int] = None, + position: int | None = None, ): """ This function is no longer in use, please use the recommended function in the warning! @@ -1043,7 +1043,7 @@ class Spotify: range_start: int, insert_before: int, range_length: int = 1, - snapshot_id: Optional[str] = None, + snapshot_id: str | None = None, ): """ This function is no longer in use, please use the recommended function in the warning! @@ -1076,7 +1076,7 @@ class Spotify: user: str, playlist_id: str, tracks: StrListOrTuple, - snapshot_id: Optional[str] = None, + snapshot_id: str | None = None, ): """ This function is no longer in use, please use the recommended function in the warning! @@ -1106,7 +1106,7 @@ class Spotify: user: str, playlist_id: str, tracks: List[TrackOccurances], - snapshot_id: Optional[str] = None, + snapshot_id: str | None = None, ): """ This function is no longer in use, please use the recommended function in the warning! @@ -1191,10 +1191,10 @@ class Spotify: def playlist_change_details( self, playlist_id: str, - name: Optional[str] = None, - public: Optional[bool] = None, - collaborative: Optional[bool] = None, - description: Optional[str] = None, + name: str | None = None, + public: bool | None = None, + collaborative: bool | None = None, + description: str | None = None, ): """ Changes a playlist's name and/or public/private state, collaborative state, and/or description @@ -1232,7 +1232,7 @@ class Spotify: ) def playlist_add_items( - self, playlist_id: str, items: StrListOrTuple, position: Optional[int] = None + self, playlist_id: str, items: StrListOrTuple, position: int | None = None ): """ Adds tracks/episodes to a playlist @@ -1269,7 +1269,7 @@ class Spotify: range_start: int, insert_before: int, range_length: int = 1, - snapshot_id: Optional[str] = None, + snapshot_id: str | None = None, ): """ Reorder tracks in a playlist @@ -1295,7 +1295,7 @@ class Spotify: ) def playlist_remove_all_occurrences_of_items( - self, playlist_id: str, items: StrListOrTuple, snapshot_id: Optional[str] = None + self, playlist_id: str, items: StrListOrTuple, snapshot_id: str | None = None ): """ Removes all occurrences of the given tracks/episodes from the given playlist @@ -1319,7 +1319,7 @@ class Spotify: self, playlist_id: str, items: List[TrackOccurances], - snapshot_id: Optional[str] = None, + snapshot_id: str | None = None, ): """ Removes all occurrences of the given tracks from the given playlist @@ -1391,7 +1391,7 @@ class Spotify: def current_user_playing_track( self, - market: Optional[str] = None, + market: str | None = None, additional_types: StrListOrTuple = ("track",) ): """ Get information about the current users currently playing track. @@ -1409,7 +1409,7 @@ class Spotify: ) def current_user_saved_albums( - self, limit: int = 20, offset: int = 0, market: Optional[str] = None + self, limit: int = 20, offset: int = 0, market: str | None = None ): """ Gets a list of the albums saved in the current authorized user's "Your Music" library @@ -1453,7 +1453,7 @@ class Spotify: return self._get("me/albums/contains?ids=" + ",".join(alist)) def current_user_saved_tracks( - self, limit: int = 20, offset: int = 0, market: Optional[str] = None + self, limit: int = 20, offset: int = 0, market: str | None = None ): """ Gets a list of the tracks saved in the current authorized user's "Your Music" library @@ -1497,7 +1497,7 @@ class Spotify: return self._get("me/tracks/contains?ids=" + ",".join(tlist)) def current_user_saved_episodes( - self, limit: int = 20, offset: int = 0, market: Optional[str] = None + self, limit: int = 20, offset: int = 0, market: str | None = None ): """ Gets a list of the episodes saved in the current authorized user's "Your Music" library @@ -1541,7 +1541,7 @@ class Spotify: return self._get("me/episodes/contains?ids=" + ",".join(elist)) def current_user_saved_shows( - self, limit: int = 20, offset: int = 0, market: Optional[str] = None + self, limit: int = 20, offset: int = 0, market: str | None = None ): """ Gets a list of the shows saved in the current authorized user's "Your Music" library @@ -1584,7 +1584,7 @@ class Spotify: return self._get("me/shows/contains?ids=" + ",".join(slist)) def current_user_followed_artists( - self, limit: int = 20, after: Optional[str] = None + self, limit: int = 20, after: str | None = None ): """ Gets a list of the artists followed by the current authorized user @@ -1655,7 +1655,7 @@ class Spotify: ) def current_user_recently_played( - self, limit: int = 50, after: Optional[int] = None, before: Optional[int] = None + self, limit: int = 50, after: int | None = None, before: int | None = None ): """ Get the current user's recently played tracks @@ -1705,9 +1705,9 @@ class Spotify: def featured_playlists( self, - locale: Optional[str] = None, - country: Optional[str] = None, - timestamp: Optional[str] = None, + locale: str | None = None, + country: str | None = None, + timestamp: str | None = None, limit: int = 20, offset: int = 0, ): @@ -1750,7 +1750,7 @@ class Spotify: ) def new_releases( - self, country: Optional[str] = None, limit: int = 20, offset: int = 0 + self, country: str | None = None, limit: int = 20, offset: int = 0 ): """ Get a list of new album releases featured in Spotify @@ -1771,8 +1771,8 @@ class Spotify: def category( self, category_id: str, - country: Optional[str] = None, - locale: Optional[str] = None, + country: str | None = None, + locale: str | None = None, ): """ Get info about a category @@ -1792,8 +1792,8 @@ class Spotify: def categories( self, - country: Optional[str] = None, - locale: Optional[str] = None, + country: str | None = None, + locale: str | None = None, limit: int = 20, offset: int = 0, ): @@ -1822,8 +1822,8 @@ class Spotify: def category_playlists( self, - category_id: Optional[str] = None, - country: Optional[str] = None, + category_id: str | None = None, + country: str | None = None, limit: int = 20, offset: int = 0, ): @@ -1858,11 +1858,11 @@ class Spotify: def recommendations( self, - seed_artists: Optional[StrListOrTuple] = None, - seed_genres: Optional[StrListOrTuple] = None, - seed_tracks: Optional[StrListOrTuple] = None, + seed_artists: StrListOrTuple | None = None, + seed_genres: StrListOrTuple | None = None, + seed_tracks: StrListOrTuple | None = None, limit: int = 20, - country: Optional[str] = None, + country: str | None = None, **kwargs ): """ Get a list of recommended tracks for one to five seeds. @@ -1995,7 +1995,7 @@ class Spotify: return self._get("me/player/devices") def current_playback( - self, market: Optional[str] = None, additional_types: Optional[str] = None + self, market: str = None, additional_types: str | None = None ): """ Get information about user's current playback. @@ -2006,7 +2006,7 @@ class Spotify: return self._get("me/player", market=market, additional_types=additional_types) def currently_playing( - self, market: Optional[str] = None, additional_types: Optional[str] = None + self, market: str | None = None, additional_types: str | None = None ): """ Get user's currently playing track. @@ -2037,11 +2037,11 @@ class Spotify: def start_playback( self, - device_id: Optional[str] = None, - context_uri: Optional[str] = None, - uris: Optional[List[str]] = None, - offset: Optional[PlaybackOffset] = None, - position_ms: Optional[Union[int, float]] = None, + device_id: str | None = None, + context_uri: str | None = None, + uris: List[str] | None = None, + offset: PlaybackOffset | None = None, + position_ms: Union[int, float] | None = None, ): """ Start or resume user's playback. @@ -2083,7 +2083,7 @@ class Spotify: self._append_device_id("me/player/play", device_id), payload=data ) - def pause_playback(self, device_id: Optional[str] = None): + def pause_playback(self, device_id: str | None = None): """ Pause user's playback. Parameters: @@ -2091,7 +2091,7 @@ class Spotify: """ return self._put(self._append_device_id("me/player/pause", device_id)) - def next_track(self, device_id: Optional[str] = None): + def next_track(self, device_id: str | None = None): """ Skip user's playback to next track. Parameters: @@ -2099,7 +2099,7 @@ class Spotify: """ return self._post(self._append_device_id("me/player/next", device_id)) - def previous_track(self, device_id: Optional[str] = None): + def previous_track(self, device_id: str | None = None): """ Skip user's playback to previous track. Parameters: @@ -2110,7 +2110,7 @@ class Spotify: ) def seek_track( - self, position_ms: Union[int, float], device_id: Optional[str] = None + self, position_ms: Union[int, float], device_id: str | None = None ): """ Seek to position in current track. @@ -2127,7 +2127,7 @@ class Spotify: ) ) - def repeat(self, state: str, device_id: Optional[str] = None): + def repeat(self, state: str, device_id: str | None = None): """ Set repeat mode for playback. Parameters: @@ -2143,7 +2143,7 @@ class Spotify: ) ) - def volume(self, volume_percent: int, device_id: Optional[str] = None): + def volume(self, volume_percent: int, device_id: str | None = None): """ Set playback volume. Parameters: @@ -2163,7 +2163,7 @@ class Spotify: ) ) - def shuffle(self, state: bool, device_id: Optional[str] = None): + def shuffle(self, state: bool, device_id: str | None = None): """ Toggle playback shuffling. Parameters: @@ -2184,7 +2184,7 @@ class Spotify: """ Gets the current user's queue """ return self._get("me/player/queue") - def add_to_queue(self, uri: str, device_id: Optional[str] = None): + def add_to_queue(self, uri: str, device_id: str | None = None): """ Adds a song to the end of a user's queue If device A is currently playing music, and you try to add to the queue @@ -2215,7 +2215,7 @@ class Spotify: """ return self._get("markets") - def _append_device_id(self, path: str, device_id: Optional[str]) -> str: + def _append_device_id(self, path: str, device_id: str | None) -> str: """ Append device ID to API path. Parameters: @@ -2271,7 +2271,7 @@ class Spotify: offset: int, type: str, markets: StrListOrTuple, - total: Optional[int], + total: int | None, ): if total and limit > total: limit = total @@ -2306,7 +2306,7 @@ class Spotify: return results - def get_audiobook(self, id: str, market: Optional[str] = None): + def get_audiobook(self, id: str, market: str | None = None): """ Get Spotify catalog information for a single audiobook identified by its unique Spotify ID. @@ -2322,7 +2322,7 @@ class Spotify: return self._get(endpoint) - def get_audiobooks(self, ids: StrListOrTuple, market: Optional[str] = None): + def get_audiobooks(self, ids: StrListOrTuple, market: str | None = None): """ Get Spotify catalog information for multiple audiobooks based on their Spotify IDs. Parameters: @@ -2338,7 +2338,7 @@ class Spotify: return self._get(endpoint) def get_audiobook_chapters( - self, id: str, market: Optional[str] = None, limit: int = 20, offset: int = 0 + self, id: str, market: str | None = None, limit: int = 20, offset: int = 0 ): """ Get Spotify catalog information about an audiobook’s chapters. diff --git a/spotipy/oauth2.py b/spotipy/oauth2.py index b96e226..c947b35 100644 --- a/spotipy/oauth2.py +++ b/spotipy/oauth2.py @@ -16,7 +16,7 @@ import urllib.parse as urllibparse import warnings import webbrowser from http.server import BaseHTTPRequestHandler, HTTPServer -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Union from urllib.parse import parse_qsl, urlparse import requests @@ -38,7 +38,7 @@ def _make_authorization_headers(client_id: str, client_secret: str): return {"Authorization": f"Basic {auth_header.decode('ascii')}"} -def _ensure_value(value: Optional[str], env_key: str) -> str: +def _ensure_value(value: str | None, env_key: str) -> str: env_val = CLIENT_CREDS_ENV_VARS[env_key] _val = value or os.getenv(env_val) if _val is None: @@ -48,7 +48,7 @@ def _ensure_value(value: Optional[str], env_key: str) -> str: class SpotifyAuthBase: - def __init__(self, requests_session: Optional[Union[requests.Session, bool]] = None): + def __init__(self, requests_session: Union[requests.Session, bool] | None = None): if isinstance(requests_session, requests.Session): self._session = requests_session else: @@ -58,7 +58,7 @@ class SpotifyAuthBase: from requests import api self._session = api - def _normalize_scope(self, scope: Optional[ScopeArgType]): + def _normalize_scope(self, scope: ScopeArgType | None): return normalize_scope(scope) @property @@ -66,7 +66,7 @@ class SpotifyAuthBase: return self._client_id @client_id.setter - def client_id(self, val: Optional[str]): + def client_id(self, val: str | None): self._client_id = _ensure_value(val, "client_id") @property @@ -74,7 +74,7 @@ class SpotifyAuthBase: return self._client_secret @client_secret.setter - def client_secret(self, val: Optional[str]): + def client_secret(self, val: str | None): self._client_secret = _ensure_value(val, "client_secret") @property @@ -82,7 +82,7 @@ class SpotifyAuthBase: return self._redirect_uri @redirect_uri.setter - def redirect_uri(self, val: Optional[str]): + def redirect_uri(self, val: str | None): self._redirect_uri = _ensure_value(val, "redirect_uri") @staticmethod @@ -99,7 +99,7 @@ class SpotifyAuthBase: @staticmethod def _is_scope_subset( - needle_scope: Optional[str], haystack_scope: Optional[str] + needle_scope: str | None, haystack_scope: str | None ) -> bool: needle_scope = set(needle_scope.split()) if needle_scope else set() haystack_scope = set(haystack_scope.split()) if haystack_scope else set() @@ -136,12 +136,12 @@ class SpotifyClientCredentials(SpotifyAuthBase): def __init__( self, - client_id: Optional[str] = None, - client_secret: Optional[str] = None, - proxies: Optional[Any] = None, + client_id: str | None = None, + client_secret: str | None = None, + proxies: Any | None = None, requests_session: Union[requests.Session, bool] = True, - requests_timeout: Optional[int] = None, - cache_handler: Optional[CacheHandler] = None, + requests_timeout: int | None = None, + cache_handler: CacheHandler | None = None, ): """ Creates a Client Credentials Flow Manager. @@ -259,19 +259,19 @@ class SpotifyOAuth(SpotifyAuthBase): def __init__( self, - client_id: Optional[str] = None, - client_secret: Optional[str] = None, - redirect_uri: Optional[str] = None, - state: Optional[Any] = None, - scope: Optional[ScopeArgType] = None, - cache_path: Optional[str] = None, - username: Optional[str] = None, - proxies: Optional[Any] = None, + client_id: str | None = None, + client_secret: str | None = None, + redirect_uri: str | None = None, + state: Any | None = None, + scope: ScopeArgType | None = None, + cache_path: str | None = None, + username: str | None = None, + proxies: Any | None = None, show_dialog: bool = False, requests_session: Union[requests.Session, bool] = True, - requests_timeout: Optional[int] = None, + requests_timeout: int | None = None, open_browser: bool = True, - cache_handler: Optional[CacheHandler] = None, + cache_handler: CacheHandler | None = None, ): """ Creates a SpotifyOAuth object @@ -339,7 +339,7 @@ class SpotifyOAuth(SpotifyAuthBase): self.show_dialog = show_dialog self.open_browser = open_browser - def validate_token(self, token_info: Optional[Dict]): + def validate_token(self, token_info: Dict | None): if token_info is None: return None @@ -356,7 +356,7 @@ class SpotifyOAuth(SpotifyAuthBase): return token_info - def get_authorize_url(self, state: Optional[Any] = None) -> str: + def get_authorize_url(self, state: Any | None = None) -> str: """ Gets the URL to use to authorize this app """ payload = { @@ -439,7 +439,7 @@ class SpotifyOAuth(SpotifyAuthBase): else: raise SpotifyOauthError("Server listening on localhost has not been accessed") - def get_auth_response(self, open_browser: Optional[bool] = None): + def get_auth_response(self, open_browser: bool | None = None): logger.info('User authentication requires interaction with your ' 'web browser. Once you enter your credentials and ' 'give authorization, you will be redirected to ' @@ -480,13 +480,13 @@ class SpotifyOAuth(SpotifyAuthBase): return self._get_auth_response_interactive(open_browser=open_browser) - def get_authorization_code(self, response: Optional[Any] = None): + def get_authorization_code(self, response: Any | None = None): if response: return self.parse_response_code(response) return self.get_auth_response() def get_access_token( - self, code: Optional[str] = None, as_dict: bool = True, check_cache: bool = True + self, code: str | None = None, as_dict: bool = True, check_cache: bool = True ): """ Gets the access token for the app given the code @@ -627,17 +627,17 @@ class SpotifyPKCE(SpotifyAuthBase): def __init__( self, - client_id: Optional[str] = None, - redirect_uri: Optional[str] = None, - state: Optional[Any] = None, - scope: Optional[ScopeArgType] = None, - cache_path: Optional[str] = None, - username: Optional[str] = None, - proxies: Optional[Any] = None, - requests_timeout: Optional[int] = None, + client_id: str | None = None, + redirect_uri: str | None = None, + state: Any | None = None, + scope: ScopeArgType | None = None, + cache_path: str | None = None, + username: str | None = None, + proxies: Any | None = None, + requests_timeout: int | None = None, requests_session: Union[requests.Session, bool] = True, open_browser: bool = True, - cache_handler: Optional[CacheHandler] = None, + cache_handler: CacheHandler | None = None, ): """ Creates Auth Manager with the PKCE Auth flow. @@ -728,7 +728,7 @@ class SpotifyPKCE(SpotifyAuthBase): code_challenge = base64.urlsafe_b64encode(code_challenge_digest).decode('utf-8') return code_challenge.replace('=', '') - def get_authorize_url(self, state: Optional[Any] = None) -> str: + def get_authorize_url(self, state: Any | None = None) -> str: """ Gets the URL to use to authorize this app """ if not self.code_challenge: self.get_pkce_handshake_parameters() @@ -748,7 +748,7 @@ class SpotifyPKCE(SpotifyAuthBase): urlparams = urllibparse.urlencode(payload) return f"{self.OAUTH_AUTHORIZE_URL}?{urlparams}" - def _open_auth_url(self, state: Optional[Any] = None): + def _open_auth_url(self, state: Any | None = None): auth_url = self.get_authorize_url(state) try: webbrowser.open(auth_url) @@ -756,7 +756,7 @@ class SpotifyPKCE(SpotifyAuthBase): except webbrowser.Error: logger.error(f"Please navigate here: {auth_url}") - def _get_auth_response(self, open_browser: Optional[bool] = None): + def _get_auth_response(self, open_browser: bool | None = None): logger.info('User authentication requires interaction with your ' 'web browser. Once you enter your credentials and ' 'give authorization, you will be redirected to ' @@ -825,7 +825,7 @@ class SpotifyPKCE(SpotifyAuthBase): raise SpotifyStateError(self.state, state) return code - def get_authorization_code(self, response: Optional[Any] = None): + def get_authorization_code(self, response: Any | None = None): if response: return self.parse_response_code(response) return self._get_auth_response() @@ -859,7 +859,7 @@ class SpotifyPKCE(SpotifyAuthBase): self.code_verifier = self._get_code_verifier() self.code_challenge = self._get_code_challenge() - def get_access_token(self, code: Optional[Any] = None, check_cache: bool = True): + def get_access_token(self, code: Any | None = None, check_cache: bool = True): """ Gets the access token for the app If the code is not given and no cached token is used, an @@ -1018,14 +1018,14 @@ class SpotifyImplicitGrant(SpotifyAuthBase): def __init__( self, - client_id: Optional[str] = None, - redirect_uri: Optional[str] = None, - state: Optional[Any] = None, - scope: Optional[ScopeArgType] = None, - cache_path: Optional[str] = None, - username: Optional[str] = None, + client_id: str | None = None, + redirect_uri: str | None = None, + state: Any | None = None, + scope: ScopeArgType | None = None, + cache_path: str | None = None, + username: str | None = None, show_dialog: bool = False, - cache_handler: Optional[CacheHandler] = None, + cache_handler: CacheHandler | None = None, ): """ Creates Auth Manager using the Implicit Grant flow @@ -1087,7 +1087,7 @@ class SpotifyImplicitGrant(SpotifyAuthBase): self.show_dialog = show_dialog self._session = None # As to not break inherited __del__ - def validate_token(self, token_info: Optional[Dict]): + def validate_token(self, token_info: Dict | None): if token_info is None: return None @@ -1104,8 +1104,8 @@ class SpotifyImplicitGrant(SpotifyAuthBase): def get_access_token( self, - state: Optional[Any] = None, - response: Optional[Any] = None, + state: Any | None = None, + response: Any | None = None, check_cache: bool = True, ): """ Gets Auth Token from cache (preferred) or user interaction @@ -1130,7 +1130,7 @@ class SpotifyImplicitGrant(SpotifyAuthBase): return token_info["access_token"] - def get_authorize_url(self, state: Optional[Any] = None) -> str: + def get_authorize_url(self, state: Any | None = None) -> str: """ Gets the URL to use to authorize this app """ payload = { "client_id": self.client_id, @@ -1150,7 +1150,7 @@ class SpotifyImplicitGrant(SpotifyAuthBase): return f"{self.OAUTH_AUTHORIZE_URL}?{urlparams}" - def parse_response_token(self, url, state: Optional[Any] = None): + def parse_response_token(self, url, state: Any | None = None): """ Parse the response code in the given response url """ remote_state, token, t_type, exp_in = self.parse_auth_response_url(url) if state is None: @@ -1175,7 +1175,7 @@ class SpotifyImplicitGrant(SpotifyAuthBase): return tuple(form.get(param) for param in ["state", "access_token", "token_type", "expires_in"]) - def _open_auth_url(self, state: Optional[Any] = None): + def _open_auth_url(self, state: Any | None = None): auth_url = self.get_authorize_url(state) try: webbrowser.open(auth_url) @@ -1183,7 +1183,7 @@ class SpotifyImplicitGrant(SpotifyAuthBase): except webbrowser.Error: logger.error(f"Please navigate here: {auth_url}") - def get_auth_response(self, state: Optional[Any] = None): + def get_auth_response(self, state: Any | None = None): """ Gets a new auth **token** with user interaction """ logger.info('User authentication requires interaction with your ' 'web browser. Once you enter your credentials and ' diff --git a/spotipy/util.py b/spotipy/util.py index 7703043..9c4874a 100644 --- a/spotipy/util.py +++ b/spotipy/util.py @@ -8,7 +8,7 @@ import logging import os import warnings from types import TracebackType -from typing import List, Optional, Tuple, Union +from typing import List, Tuple, Union import requests import urllib3 @@ -31,15 +31,15 @@ StrListOrTuple = Union[List[str], Tuple[str, ...]] def prompt_for_user_token( - username: Optional[str] = None, - scope: Optional[str] = None, - client_id: Optional[str] = None, - client_secret: Optional[str] = None, - redirect_uri: Optional[str] = None, - cache_path: Optional[str] = None, - oauth_manager: Optional[spotipy.SpotifyOAuth] = None, + username: str | None = None, + scope: str | None = None, + client_id: str | None = None, + client_secret: str | None = None, + redirect_uri: str | None = None, + cache_path: str | None = None, + oauth_manager: spotipy.SpotifyOAuth | None = None, show_dialog: bool = False, -) -> Union[str, None]: +) -> str | None: """ Prompt the user to login if necessary and returns a user token suitable for use with the spotipy.Spotify constructor. @@ -139,7 +139,7 @@ def get_host_port(netloc: str): ScopeArgType = Union[str, StrListOrTuple] -def normalize_scope(scope: Optional[ScopeArgType]) -> Union[str, None]: +def normalize_scope(scope: ScopeArgType | None) -> str | None: """Normalize the scope to verify that it is a list or tuple. A string input will split the string by commas to create a list of scopes. A list or tuple input is used directly. @@ -170,12 +170,12 @@ class Retry(urllib3.Retry): def increment( self, - method: Optional[str] = None, - url: Optional[str] = None, - response: Optional[urllib3.BaseHTTPResponse] = None, - error: Optional[Exception] = None, - _pool: Optional[urllib3.connectionpool.ConnectionPool] = None, - _stacktrace: Optional[TracebackType] = None, + 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")