Merge branch 'master' of github.com:spotipy-dev/spotipy into v3

This commit is contained in:
Stéphane Bruckert 2025-01-19 16:10:51 +00:00
commit 71fe07a5d1
64 changed files with 181 additions and 112 deletions

View File

@ -9,17 +9,19 @@ jobs:
env:
SPOTIPY_CLIENT_ID: ${{ secrets.SPOTIPY_CLIENT_ID }}
SPOTIPY_CLIENT_SECRET: ${{ secrets.SPOTIPY_CLIENT_SECRET }}
PYTHON_VERSION: "3.10"
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ env.PYTHON_VERSION }}
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .[test]
pip install .
- name: Run non user endpoints integration tests
run: |
python -m unittest discover -v tests/integration/non_user_endpoints

24
.github/workflows/lint.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Lint
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x" # Lint can be done on latest Python only
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .[test]
- name: Check pep8 with flake8
run: |
flake8 . --count --show-source --statistics
- name: Check sorted imports with isort
run: |
isort . -c

View File

@ -1,6 +1,6 @@
name: Tests
name: Unit tests
on: [push, pull_request]
on: [push]
jobs:
build:
@ -18,12 +18,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .[test]
- name: Lint with flake8
run: |
pip install -Iv enum34==1.1.6 # https://bitbucket.org/stoneleaf/enum34/issues/27/enum34-118-broken
pip install flake8
flake8 . --count --show-source --statistics
pip install .
- name: Run unit tests
run: |
python -m unittest discover -v tests/unit

View File

@ -85,6 +85,10 @@ Rebasing master onto v3 doesn't require a changelog update.
- Added FAQ entry for inaccessible playlists
- Type annotations to `spotipy.cache_handler`
### Changed
- Split test and lint workflows
### Fixed
- Audiobook integration tests

View File

@ -29,7 +29,7 @@ $ source env/bin/activate
### Lint
To automatically fix the code style:
To automatically fix some of the code style:
pip install autopep8
autopep8 --in-place --aggressive --recursive .
@ -42,7 +42,11 @@ To verify the code style:
To make sure if the import lists are stored correctly:
pip install isort
isort . -c -v
isort . -c
Sort them automatically with:
isort .
### Changelog

View File

@ -10,8 +10,8 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
import sys
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the

View File

@ -4,6 +4,7 @@ Usage: add_saved_episodes.py -e episode_id episode_id ...
"""
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth

View File

@ -4,6 +4,7 @@ Usage: add_saved_shows.py -s show_id show_id ...
"""
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth

View File

@ -24,8 +24,10 @@ Run app.py
"""
import os
from flask import Flask, session, request, redirect
from flask import Flask, redirect, request, session
from flask_session import Session
import spotipy
app = Flask(__name__)

View File

@ -1,8 +1,8 @@
import argparse
import logging
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
logger = logging.getLogger('examples.artist_albums')
logging.basicConfig(level='INFO')

View File

@ -2,8 +2,8 @@
import argparse
import logging
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
logger = logging.getLogger('examples.artist_discography')
logging.basicConfig(level='INFO')

View File

@ -4,7 +4,6 @@ import logging
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
logger = logging.getLogger('examples.artist_recommendations')
logging.basicConfig(level='INFO')

View File

@ -1,11 +1,11 @@
# shows audio analysis for the given track
from spotipy.oauth2 import SpotifyClientCredentials
import json
import spotipy
import time
import sys
import time
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
auth_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(auth_manager=auth_manager)

View File

@ -1,11 +1,11 @@
# shows acoustic features for tracks for the given artist
from spotipy.oauth2 import SpotifyClientCredentials
import json
import spotipy
import time
import sys
import time
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
auth_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(auth_manager=auth_manager)

View File

@ -1,12 +1,12 @@
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
import matplotlib.pyplot as plt
# Import the extra necessary libraries for this example
# These libraries are not included in the default packages
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
# Set up Spotify credentials
client_credentials_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)

View File

@ -1,11 +1,11 @@
# shows acoustic features for tracks for the given artist
from spotipy.oauth2 import SpotifyClientCredentials
import json
import spotipy
import time
import sys
import time
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
auth_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(auth_manager=auth_manager)

View File

@ -4,6 +4,7 @@ Usage: check_show_is_saved -s show_id show_id ...
"""
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth

View File

@ -1,7 +1,8 @@
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
from pprint import pprint
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
auth_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(auth_manager=auth_manager)

View File

@ -4,6 +4,7 @@ Usage: delete_saved_episodes.py -e episode_id episode_id ...
"""
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth

View File

@ -1,6 +1,7 @@
# Follow a playlist
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth

View File

@ -4,6 +4,7 @@ Usage: get_audiobooks_chapters_info.py -a audiobook_id
"""
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth

View File

@ -4,9 +4,11 @@ Usage: get_audiobooks_info.py -a audiobook_id audiobook_id ...
"""
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth
def get_args():
parser = argparse.ArgumentParser(description='Get information for a list of audiobooks')
# Defaults set to The Great Gatsby, The Chronicles of Narnia and Dune

View File

@ -1,5 +1,4 @@
import spotipy
from spotipy.oauth2 import SpotifyOAuth
# set open_browser=False to prevent Spotipy from attempting to open the default browser

View File

@ -0,0 +1,10 @@
from pprint import pprint
import spotipy
import spotipy.util as util
while True:
username = input("Type the Spotify user ID to use: ")
token = util.prompt_for_user_token(username, show_dialog=True)
sp = spotipy.Spotify(token)
pprint(sp.me())

View File

@ -1,11 +1,11 @@
import spotipy
from spotipy.oauth2 import SpotifyOAuth
# Import the extra necessary libraries for this example
# These libraries are not included in the default packages
import pandas as pd
from sklearn.cluster import KMeans
import spotipy
from spotipy.oauth2 import SpotifyOAuth
# Set up Spotify credentials
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(
client_id="YOUR_APP_CLIENT_ID",

View File

@ -1,8 +1,9 @@
import spotipy
from spotipy.oauth2 import SpotifyOAuth
from pprint import pprint
from time import sleep
import spotipy
from spotipy.oauth2 import SpotifyOAuth
scope = "user-read-playback-state,user-modify-playback-state"
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))

View File

@ -1,6 +1,6 @@
# get all non-local tracks of a playlist
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
# playlist id of global top 50
PlaylistExample = '37i9dQZEVXbMDoHDwVN2tF'

View File

@ -1,8 +1,10 @@
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
from pprint import pprint
sp = spotipy.Spotify(auth_manager=SpotifyClientCredentials())
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
auth_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(auth_manager=auth_manager)
pl_id = 'spotify:playlist:5RIbzhG2QqdkaP24iXLnZX'
offset = 0

View File

@ -1,7 +1,8 @@
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
import json
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
auth_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(auth_manager=auth_manager)

View File

@ -5,7 +5,6 @@ import sys
import spotipy
from spotipy.oauth2 import SpotifyOAuth
if len(sys.argv) > 2:
playlist_id = sys.argv[1]
track_ids = sys.argv[2:]

View File

@ -1,9 +1,10 @@
# shows artist info for a URN or URL
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
import sys
import pprint
import sys
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
search_str = sys.argv[1] if len(sys.argv) > 1 else 'Radiohead'
sp = spotipy.Spotify(auth_manager=SpotifyClientCredentials())

View File

@ -1,10 +1,11 @@
# shows album info for a URN or URL
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
import sys
from pprint import pprint
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
if len(sys.argv) > 1:
urn = sys.argv[1]
else:

View File

@ -1,10 +1,11 @@
# shows artist info for a URN or URL
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
import sys
from pprint import pprint
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
if len(sys.argv) > 1:
urn = sys.argv[1]
else:

View File

@ -1,10 +1,11 @@
# shows artist info for a URN or URL
# scope is not required for this function
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
import sys
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
if len(sys.argv) > 1:
urn = sys.argv[1]
else:

View File

@ -2,10 +2,11 @@
# usage: python tracks.py [artist name]
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
import sys
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
auth_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(auth_manager=auth_manager)

View File

@ -2,11 +2,12 @@
# usage: featured_artists.py spotify:album:[album urn]
from spotipy.oauth2 import SpotifyClientCredentials
import sys
import spotipy
from pprint import pprint
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
if len(sys.argv) > 1:
urn = sys.argv[1]
else:

View File

@ -1,13 +1,15 @@
# shows related artists for the given seed artist
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
import sys
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
artist_name = sys.argv[1] if len(sys.argv) > 1 else 'weezer'
auth_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(auth_manager=auth_manager)
result = sp.search(q=f'artist:{artist_name}', type='artist')
try:
name = result['artists']['items'][0]['name']
uri = result['artists']['items'][0]['uri']

View File

@ -1,10 +1,11 @@
# shows track info for a URN or URL
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
import sys
from pprint import pprint
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
if len(sys.argv) > 1:
urn = sys.argv[1]
else:

View File

@ -3,10 +3,11 @@
given a list of track IDs show the artist and track name
'''
from spotipy.oauth2 import SpotifyOAuth
import spotipy
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth
def get_args():
parser = argparse.ArgumentParser(description='Print artist and track name given a list of track IDs')

View File

@ -1,9 +1,10 @@
# Shows artist info for a URN or URL
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
import sys
import pprint
import sys
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
username = sys.argv[1] if len(sys.argv) > 1 else 'plamere'
auth_manager = SpotifyClientCredentials()

View File

@ -1,5 +1,5 @@
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
birdy_uri = 'spotify:artist:2WX2uTcsvV5OnS0inACecP'

View File

@ -1,5 +1,5 @@
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
lz_uri = 'spotify:artist:36QJpDe2go2KgaRleHCDTp'

View File

@ -1,6 +1,7 @@
import spotipy
from pprint import pprint
import spotipy
def main():
spotify = spotipy.Spotify(auth_manager=spotipy.SpotifyOAuth())

View File

@ -1,5 +1,5 @@
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
auth_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(auth_manager=auth_manager)

View File

@ -1,8 +1,8 @@
# Shows the name of the artist/band and their image by giving a link
import sys
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
sp = spotipy.Spotify(auth_manager=SpotifyClientCredentials())

View File

@ -1,7 +1,8 @@
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
import random
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
'''
generates a list of songs where the first word in each subsequent song
matches the last word of the previous song.

View File

@ -1,6 +1,7 @@
import random
import spotipy
from spotipy.oauth2 import SpotifyOAuth
import random
# Set up Spotify credentials
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(

View File

@ -1,6 +1,7 @@
# Shows a user's playlists (need to be authenticated via oauth)
import sys
import spotipy
from spotipy.oauth2 import SpotifyOAuth

View File

@ -3,6 +3,7 @@
#
import sys
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials

View File

@ -4,6 +4,7 @@ Usage: user_saved_episodes -l <num> -o <num>
"""
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth

View File

@ -4,6 +4,7 @@ Usage: user_saved_shows -l <num> -o <num>
"""
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth

View File

@ -8,7 +8,13 @@ memcache_cache_reqs = [
]
extra_reqs = {
'memcache': memcache_cache_reqs
'memcache': [
'pymemcache>=3.5.2'
],
'test': [
'flake8>=7.1.1',
'isort>=5.13.2'
]
}
setup(

View File

@ -2,5 +2,5 @@ from .cache_handler import * # noqa
from .client import * # noqa
from .exceptions import * # noqa
from .oauth2 import * # noqa
from .util import * # noqa
from .scope import * # noqa
from .util import * # noqa

View File

@ -7,9 +7,10 @@ import os
from abc import ABC, abstractmethod
from json import JSONEncoder
from typing import TypedDict
import redis
from redis import RedisError
import redis.client
from redis import RedisError
from .util import CLIENT_CREDS_ENV_VARS

View File

@ -6,14 +6,13 @@ import json
import logging
import re
import warnings
from collections import defaultdict
import requests
from spotipy.exceptions import SpotifyException
from spotipy.util import Retry
from collections import defaultdict
logger = logging.getLogger(__name__)

View File

@ -9,20 +9,20 @@ __all__ = [
import base64
import logging
import os
import re
import time
import urllib.parse as urllibparse
import webbrowser
from http.server import BaseHTTPRequestHandler, HTTPServer
from typing import Iterable
from urllib.parse import parse_qsl, urlparse
import requests
import urllib.parse as urllibparse
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import parse_qsl, urlparse
from spotipy.cache_handler import CacheFileHandler, CacheHandler
from spotipy.exceptions import SpotifyOauthError, SpotifyStateError
from spotipy.util import CLIENT_CREDS_ENV_VARS, get_host_port
from spotipy.scope import Scope
from typing import Iterable
import re
from spotipy.util import CLIENT_CREDS_ENV_VARS, get_host_port
logger = logging.getLogger(__name__)

View File

@ -2,8 +2,8 @@
__all__ = ["Scope"]
from enum import Enum
import re
from enum import Enum
from typing import Iterable, Set

View File

@ -5,8 +5,8 @@ from spotipy.scope import Scope
__all__ = ["CLIENT_CREDS_ENV_VARS", "get_host_port", "normalize_scope", "Retry"]
import logging
from types import TracebackType
from collections.abc import Iterable
from types import TracebackType
import urllib3

View File

@ -1,4 +1,5 @@
import base64
import requests

View File

@ -1,12 +1,10 @@
from spotipy import (
Spotify,
SpotifyClientCredentials,
SpotifyException
)
import spotipy
import unittest
import requests
import spotipy
from spotipy import Spotify, SpotifyClientCredentials, SpotifyException
class AuthTestSpotipy(unittest.TestCase):
"""

View File

@ -1,14 +1,9 @@
import os
from spotipy import (
CLIENT_CREDS_ENV_VARS as CCEV,
Spotify,
SpotifyException,
SpotifyOAuth,
SpotifyPKCE,
CacheFileHandler
)
import unittest
from spotipy import CLIENT_CREDS_ENV_VARS as CCEV
from spotipy import (CacheFileHandler, Spotify, SpotifyException, SpotifyOAuth,
SpotifyPKCE)
from tests import helpers

View File

@ -1,14 +1,13 @@
import io
import json
import unittest
import unittest.mock as mock
import urllib.parse as urllibparse
from spotipy import SpotifyOAuth, SpotifyPKCE
from spotipy.oauth2 import SpotifyClientCredentials, SpotifyOauthError
from spotipy.oauth2 import SpotifyStateError
from spotipy import MemoryCacheHandler, CacheFileHandler
from spotipy.cache_handler import CacheFileHandler, MemoryCacheHandler
from spotipy.oauth2 import (SpotifyClientCredentials, SpotifyOauthError,
SpotifyStateError)
patch = mock.patch
DEFAULT = mock.DEFAULT
@ -323,8 +322,8 @@ class TestSpotifyPKCE(unittest.TestCase):
self.assertTrue(auth.code_challenge)
def test_code_verifier_and_code_challenge_are_correct(self):
import hashlib
import base64
import hashlib
auth = SpotifyPKCE("CLID", "REDIR")
auth.get_pkce_handshake_parameters()
self.assertEqual(auth.code_challenge,

View File

@ -1,6 +1,7 @@
from unittest import TestCase
from spotipy.scope import Scope
from spotipy.oauth2 import SpotifyAuthBase
from spotipy.scope import Scope
class SpotipyScopeTest(TestCase):

View File

@ -3,7 +3,7 @@ envlist = py3{8,9,10,11,12}
[testenv]
deps=
requests
commands=python -m unittest discover -v tests
commands=python -m unittest discover -v tests/unit
[flake8]
max-line-length = 99
exclude=