Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-spotipy for openSUSE:Factory 
checked in at 2023-12-15 21:50:38
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-spotipy (Old)
 and      /work/SRC/openSUSE:Factory/.python-spotipy.new.25432 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-spotipy"

Fri Dec 15 21:50:38 2023 rev:11 rq:1133445 version:2.23.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-spotipy/python-spotipy.changes    
2022-12-15 19:24:55.643961472 +0100
+++ /work/SRC/openSUSE:Factory/.python-spotipy.new.25432/python-spotipy.changes 
2023-12-15 21:51:03.138458518 +0100
@@ -1,0 +2,25 @@
+Fri Dec  8 13:45:17 UTC 2023 - Dirk Müller <dmuel...@suse.com>
+
+- update to 2.23.0:
+  * Added optional `encoder_cls` argument to `CacheFileHandler`,
+    which overwrite default encoder for token before writing to
+    disk
+  * Integration tests for searching multiple types in multiple
+    markets (non-user endpoints)
+  * Publish to PyPI action
+  * Fixed the regex for matching playlist URIs with the format
+    spotify:user:USERNAME:playlist:PLAYLISTID.
+  * `search_markets` now factors the counts of all types in the
+    `total`  rather than just the first type (#534)
+  * Add alternative module installation instruction to README
+  * Added Comment to README - Getting Started for user to add URI
+    to app in Spotify Developer Dashboard.
+  * Added playlist_add_tracks.py to example folder
+  * Modified docstring for playlist_add_items() to accept "only
+    URIs or URLs",
+  * with intended deprecation for IDs in v3
+  * Path traversal vulnerability that may lead to type confusion
+    in URI handling code
+  * Update contributing.md
+
+-------------------------------------------------------------------

Old:
----
  2.22.0.tar.gz

New:
----
  2.23.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-spotipy.spec ++++++
--- /var/tmp/diff_new_pack.8Ug8p7/_old  2023-12-15 21:51:03.682478412 +0100
+++ /var/tmp/diff_new_pack.8Ug8p7/_new  2023-12-15 21:51:03.686478559 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-spotipy
 #
-# Copyright (c) 2022 SUSE LLC
+# Copyright (c) 2023 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-spotipy
-Version:        2.22.0
+Version:        2.23.0
 Release:        0
 Summary:        Client for the Spotify Web API
 License:        MIT

++++++ 2.22.0.tar.gz -> 2.23.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/.github/SECURITY.md 
new/spotipy-2.23.0/.github/SECURITY.md
--- old/spotipy-2.22.0/.github/SECURITY.md      1970-01-01 01:00:00.000000000 
+0100
+++ new/spotipy-2.23.0/.github/SECURITY.md      2023-04-07 19:36:17.000000000 
+0200
@@ -0,0 +1,14 @@
+# Security Policy
+
+## Supported Versions
+
+| Version | Supported          |
+| ------- | ------------------ |
+| 2.x     | :white_check_mark: |
+| 1.x     | :x:                |
+
+## Reporting a Vulnerability
+
+Report via https://github.com/spotipy-dev/spotipy/security/advisories.
+
+Guidance: 
https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/.github/workflows/publish.yml 
new/spotipy-2.23.0/.github/workflows/publish.yml
--- old/spotipy-2.22.0/.github/workflows/publish.yml    1970-01-01 
01:00:00.000000000 +0100
+++ new/spotipy-2.23.0/.github/workflows/publish.yml    2023-04-07 
19:36:17.000000000 +0200
@@ -0,0 +1,57 @@
+name: Publish to PyPI
+
+on:
+  push:
+    branches-ignore:
+      - '**'
+    tags:
+      - '*.*.*'
+
+jobs:
+  build-n-publish:
+    name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v3
+      - name: Set up Python
+        uses: actions/setup-python@v4
+        with:
+          python-version: "3.x"
+      - name: Install pypa/build
+        run: >-
+          python -m
+          pip install
+          build
+          --user
+      - name: Build a binary wheel and a source tarball
+        run: >-
+          python -m
+          build
+          --sdist
+          --wheel
+          --outdir dist/
+          .
+      - name: Set up Python
+        uses: actions/setup-python@v4
+        with:
+          python-version: "2.x"
+      - name: Install pypa/build
+        run: >-
+          python -m
+          pip install
+          build
+          --user
+      - name: Build a binary wheel and a source tarball
+        run: >-
+          python -m
+          build
+          --sdist
+          --wheel
+          --outdir dist/
+          .
+      - name: Publish distribution 📦 to PyPI
+        if: startsWith(github.ref, 'refs/tags')
+        uses: pypa/gh-action-pypi-publish@release/v1
+        with:
+          password: ${{ secrets.PYPI_API_TOKEN }}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/CHANGELOG.md 
new/spotipy-2.23.0/CHANGELOG.md
--- old/spotipy-2.22.0/CHANGELOG.md     2022-12-10 16:31:47.000000000 +0100
+++ new/spotipy-2.23.0/CHANGELOG.md     2023-04-07 19:36:17.000000000 +0200
@@ -7,14 +7,44 @@
 
 ## Unreleased
 
-// Add new changes below this line
+### Added
+- Replace with changes
+
+### Fixed
+
+### Removed
+
+## [2.23.0] - 2023-04-07
 
 ### Added
+- Added optional `encoder_cls` argument to `CacheFileHandler`, which overwrite 
default encoder for token before writing to disk
+- Integration tests for searching multiple types in multiple markets (non-user 
endpoints)
+- Publish to PyPI action
 
 ### Fixed
+- Fixed the regex for matching playlist URIs with the format 
spotify:user:USERNAME:playlist:PLAYLISTID.
+- `search_markets` now factors the counts of all types in the `total`  rather 
than just the first type 
([#534](https://github.com/spotipy-dev/spotipy/issues/534))
 
 ### Removed
 
+## [2.22.1] - 2023-01-23
+
+### Added
+
+- Add alternative module installation instruction to README
+- Added Comment to README - Getting Started for user to add URI to app in 
Spotify Developer Dashboard. 
+- Added playlist_add_tracks.py to example folder
+
+### Changed
+
+- Modified docstring for playlist_add_items() to accept "only URIs or URLs",
+  with intended deprecation for IDs in v3
+
+### Fixed
+
+- Path traversal vulnerability that may lead to type confusion in URI handling 
code
+- Update contributing.md
+
 ## [2.22.0] - 2022-12-10
 
 ### Added
@@ -375,6 +405,9 @@
 - Fix typos in doc
 - Start following [SemVer](https://semver.org) properly
 
+### Changed
+
+- Made instructions in the CONTRIBUTING.md file more clear such that it is 
easier to onboard and there are no conflicts with TUTORIAL.md
 ## [2.5.0] - 2020-01-11
 
 Added follow and player endpoints
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/CONTRIBUTING.md 
new/spotipy-2.23.0/CONTRIBUTING.md
--- old/spotipy-2.22.0/CONTRIBUTING.md  2022-12-10 16:31:47.000000000 +0100
+++ new/spotipy-2.23.0/CONTRIBUTING.md  2023-04-07 19:36:17.000000000 +0200
@@ -8,7 +8,7 @@
 # Linux or Mac
 export SPOTIPY_CLIENT_ID=client_id_here
 export SPOTIPY_CLIENT_SECRET=client_secret_here
-export SPOTIPY_CLIENT_USERNAME=client_username_here # This is actually an id 
not spotify display name
+export SPOTIPY_CLIENT_USERNAME=client_username_here # This is actually an id 
not spotify display name and can be found 
[here](https://www.spotify.com/us/account/overview/)
 export SPOTIPY_REDIRECT_URI=http://localhost:8080 # Make url is set in app you 
created to get your ID and SECRET
 
 # Windows
@@ -21,9 +21,9 @@
 ### Create virtual environment, install dependencies, run tests:
 
 ```bash
-$ virtualenv --python=python3.7 env
+$ virtualenv --python=python3 env
 $ source env/bin/activate
-(env) $ pip install --user -e .
+(env) $ pip install -e . 
 (env) $ python -m unittest discover -v tests
 ```
 
@@ -44,6 +44,10 @@
     pip install isort
     isort . -c -v
 
+### Changelog
+
+Don't forget to add a short description of your change in the 
[CHANGELOG](CHANGELOG.md)
+
 ### Publishing (by maintainer)
 
  - Bump version in setup.py
@@ -52,20 +56,15 @@
 
        ## Unreleased
 
-       // Add your changes here and then delete this line
+       ### Added
+       - Replace with changes
 
- - Commit changes
- - Package to pypi:
+       ### Fixed
 
-       python setup.py sdist bdist_wheel
-       python3 setup.py sdist bdist_wheel
-       twine check dist/*
-       twine upload --repository-url https://upload.pypi.org/legacy/ 
--skip-existing dist/*.(whl|gz|zip)~dist/*linux*.whl
+       ### Removed
 
+ - Commit changes
+ - Push tag to trigger PyPI build & release workflow
  - Create github release https://github.com/plamere/spotipy/releases with the 
changelog content
    for the version and a short name that describes the main addition
  - Verify doc uses latest https://readthedocs.org/projects/spotipy/
-
-### Changelog
-
-Don't forget to add a short description of your change in the 
[CHANGELOG](CHANGELOG.md)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/README.md new/spotipy-2.23.0/README.md
--- old/spotipy-2.22.0/README.md        2022-12-10 16:31:47.000000000 +0100
+++ new/spotipy-2.23.0/README.md        2023-04-07 19:36:17.000000000 +0200
@@ -14,6 +14,12 @@
 pip install spotipy
 ```
 
+alternatively, for Windows users 
+
+```bash
+py -m pip install spotipy
+```
+
 or upgrade
 
 ```bash
@@ -43,6 +49,8 @@
 
 ### With user authentication
 
+A redirect URI must be added to your application at [My 
Dashboard](https://developer.spotify.com/dashboard/applications) to access user 
authenticated features.
+
 ```python
 import spotipy
 from spotipy.oauth2 import SpotifyOAuth
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/docs/conf.py 
new/spotipy-2.23.0/docs/conf.py
--- old/spotipy-2.22.0/docs/conf.py     2022-12-10 16:31:47.000000000 +0100
+++ new/spotipy-2.23.0/docs/conf.py     2023-04-07 19:36:17.000000000 +0200
@@ -11,14 +11,15 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import sys, os
+import spotipy
+import sys
+import os
 
 # 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
 # documentation root, use os.path.abspath to make it absolute, like shown here.
 #sys.path.insert(0, os.path.abspath('.'))
 sys.path.insert(0, os.path.abspath('.'))
-import spotipy
 
 # -- General configuration 
-----------------------------------------------------
 
@@ -172,21 +173,21 @@
 # -- Options for LaTeX output 
--------------------------------------------------
 
 latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
+    # The paper size ('letterpaper' or 'a4paper').
+    # 'papersize': 'letterpaper',
 
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
+    # The font size ('10pt', '11pt' or '12pt').
+    # 'pointsize': '10pt',
 
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
+    # Additional stuff for the LaTeX preamble.
+    # 'preamble': '',
 }
 
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass 
[howto/manual]).
 latex_documents = [
-  ('index', 'spotipy.tex', 'spotipy Documentation',
-   'Paul Lamere', 'manual'),
+    ('index', 'spotipy.tex', 'spotipy Documentation',
+     'Paul Lamere', 'manual'),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
@@ -229,9 +230,9 @@
 # (source start file, target name, title, author,
 #  dir menu entry, description, category)
 texinfo_documents = [
-  ('index', 'spotipy', 'spotipy Documentation',
-   'Paul Lamere', 'spotipy', 'One line description of project.',
-   'Miscellaneous'),
+    ('index', 'spotipy', 'spotipy Documentation',
+     'Paul Lamere', 'spotipy', 'One line description of project.',
+     'Miscellaneous'),
 ]
 
 # Documents to append as an appendix to all manuals.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/docs/index.rst 
new/spotipy-2.23.0/docs/index.rst
--- old/spotipy-2.22.0/docs/index.rst   2022-12-10 16:31:47.000000000 +0100
+++ new/spotipy-2.23.0/docs/index.rst   2023-04-07 19:36:17.000000000 +0200
@@ -9,7 +9,7 @@
 you get full access to all of the music data provided by the Spotify platform.
 
 Assuming you set the ``SPOTIPY_CLIENT_ID`` and ``SPOTIPY_CLIENT_SECRET``
-environment variables (here is a `video <https://youtu.be/3RGm4jALukM>`_ 
explaining how to do so), here's a quick example of using *Spotipy* to list the
+environment variables (here is a `video <https://youtu.be/3RGm4jALukM>`_ 
explaining how to do so). For a longer tutorial with examples included, refer 
to this `video playlist 
<https://www.youtube.com/watch?v=tmt5SdvTqUI&list=PLqgOPibB_QnzzcaOFYmY2cQjs35y0is9N&index=1>`_.
 Below is a quick example of using *Spotipy* to list the
 names of all the albums released by the artist 'Birdy'::
 
     import spotipy
@@ -160,7 +160,7 @@
 such as ``http://example.com``, ``http://localhost`` or 
``http://127.0.0.1:9090``.
 
     .. note:: If you choose an `http`-scheme URL, and it's for `localhost` or
-     `127.0.0.1`, **AND** it specifies a port, then spotispy will instantiate
+     `127.0.0.1`, **AND** it specifies a port, then spotipy will instantiate
       a server on the indicated response to receive the access token from the
       response at the end of the oauth flow [see the 
code](https://github.com/plamere/spotipy/blob/master/spotipy/oauth2.py#L483-L490).
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/add_tracks_to_playlist.py 
new/spotipy-2.23.0/examples/add_tracks_to_playlist.py
--- old/spotipy-2.22.0/examples/add_tracks_to_playlist.py       2022-12-10 
16:31:47.000000000 +0100
+++ new/spotipy-2.23.0/examples/add_tracks_to_playlist.py       2023-04-07 
19:36:17.000000000 +0200
@@ -11,7 +11,7 @@
 
 def get_args():
     parser = argparse.ArgumentParser(description='Adds track to user playlist')
-    parser.add_argument('-t', '--tids', action='append',
+    parser.add_argument('-u', '--uris', action='append',
                         required=True, help='Track ids')
     parser.add_argument('-p', '--playlist', required=True,
                         help='Playlist to add track to')
@@ -22,7 +22,7 @@
     args = get_args()
 
     sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
-    sp.playlist_add_items(args.playlist, args.tids)
+    sp.playlist_add_items(args.playlist, args.uris)
 
 
 if __name__ == '__main__':
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/app.py 
new/spotipy-2.23.0/examples/app.py
--- old/spotipy-2.22.0/examples/app.py  2022-12-10 16:31:47.000000000 +0100
+++ new/spotipy-2.23.0/examples/app.py  2023-04-07 19:36:17.000000000 +0200
@@ -13,7 +13,7 @@
     export FLASK_ENV=development
     // so that you can invoke the app outside of the file's directory include
     export FLASK_APP=/path/to/spotipy/examples/app.py
- 
+
     // on Windows, use `SET` instead of `export`
 
 Run app.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/artist_discography.py 
new/spotipy-2.23.0/examples/artist_discography.py
--- old/spotipy-2.22.0/examples/artist_discography.py   2022-12-10 
16:31:47.000000000 +0100
+++ new/spotipy-2.23.0/examples/artist_discography.py   2023-04-07 
19:36:17.000000000 +0200
@@ -1,4 +1,4 @@
-#Shows the list of all songs sung by the artist or the band
+# Shows the list of all songs sung by the artist or the band
 import argparse
 import logging
 
@@ -34,7 +34,7 @@
         results = sp.next(results)
         tracks.extend(results['items'])
     for i, track in enumerate(tracks):
-        logger.info('%s. %s', i+1, track['name'])
+        logger.info('%s. %s', i + 1, track['name'])
 
 
 def show_artist_albums(artist):
@@ -60,6 +60,7 @@
     if len(artist['genres']) > 0:
         logger.info('Genres: %s', ','.join(artist['genres']))
 
+
 def main():
     args = get_args()
     artist = get_artist(args.artist)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/follow_playlist.py 
new/spotipy-2.23.0/examples/follow_playlist.py
--- old/spotipy-2.22.0/examples/follow_playlist.py      2022-12-10 
16:31:47.000000000 +0100
+++ new/spotipy-2.23.0/examples/follow_playlist.py      2023-04-07 
19:36:17.000000000 +0200
@@ -3,21 +3,25 @@
 import spotipy
 from spotipy.oauth2 import SpotifyOAuth
 
+
 def get_args():
     parser = argparse.ArgumentParser(description='Follows a playlist based on 
playlist ID')
     parser.add_argument('-p', '--playlist', required=True, help='Playlist ID')
-    
+
     return parser.parse_args()
 
+
 def main():
     args = get_args()
-    
+
     if args.playlist is None:
-      # Uses the Spotify Global Top 50 playlist
-      
spotipy.Spotify(auth_manager=SpotifyOAuth()).current_user_follow_playlist('37i9dQZEVXbMDoHDwVN2tF')
-      
+        # Uses the Spotify Global Top 50 playlist
+        
spotipy.Spotify(auth_manager=SpotifyOAuth()).current_user_follow_playlist(
+            '37i9dQZEVXbMDoHDwVN2tF')
+
     else:
-      
spotipy.Spotify(auth_manager=SpotifyOAuth()).current_user_follow_playlist(args.playlist)
-    
+        
spotipy.Spotify(auth_manager=SpotifyOAuth()).current_user_follow_playlist(args.playlist)
+
+
 if __name__ == '__main__':
     main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/headless.py 
new/spotipy-2.23.0/examples/headless.py
--- old/spotipy-2.22.0/examples/headless.py     2022-12-10 16:31:47.000000000 
+0100
+++ new/spotipy-2.23.0/examples/headless.py     2023-04-07 19:36:17.000000000 
+0200
@@ -5,4 +5,4 @@
 # set open_browser=False to prevent Spotipy from attempting to open the 
default browser
 spotify = spotipy.Spotify(auth_manager=SpotifyOAuth(open_browser=False))
 
-print(spotify.me())
\ No newline at end of file
+print(spotify.me())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/multiple_accounts.py 
new/spotipy-2.23.0/examples/multiple_accounts.py
--- old/spotipy-2.22.0/examples/multiple_accounts.py    2022-12-10 
16:31:47.000000000 +0100
+++ new/spotipy-2.23.0/examples/multiple_accounts.py    2023-04-07 
19:36:17.000000000 +0200
@@ -7,4 +7,4 @@
     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())
\ No newline at end of file
+    pprint(sp.me())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/my_playlists.py 
new/spotipy-2.23.0/examples/my_playlists.py
--- old/spotipy-2.22.0/examples/my_playlists.py 2022-12-10 16:31:47.000000000 
+0100
+++ new/spotipy-2.23.0/examples/my_playlists.py 2023-04-07 19:36:17.000000000 
+0200
@@ -8,4 +8,4 @@
 
 results = sp.current_user_playlists(limit=50)
 for i, item in enumerate(results['items']):
-    print("%d %s" % (i, item['name']))
\ No newline at end of file
+    print("%d %s" % (i, item['name']))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/my_top_tracks.py 
new/spotipy-2.23.0/examples/my_top_tracks.py
--- old/spotipy-2.22.0/examples/my_top_tracks.py        2022-12-10 
16:31:47.000000000 +0100
+++ new/spotipy-2.23.0/examples/my_top_tracks.py        2023-04-07 
19:36:17.000000000 +0200
@@ -13,4 +13,4 @@
     results = sp.current_user_top_tracks(time_range=sp_range, limit=50)
     for i, item in enumerate(results['items']):
         print(i, item['name'], '//', item['artists'][0]['name'])
-    print()
\ No newline at end of file
+    print()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/playlist_add_items.py 
new/spotipy-2.23.0/examples/playlist_add_items.py
--- old/spotipy-2.22.0/examples/playlist_add_items.py   1970-01-01 
01:00:00.000000000 +0100
+++ new/spotipy-2.23.0/examples/playlist_add_items.py   2023-04-07 
19:36:17.000000000 +0200
@@ -0,0 +1,12 @@
+# Add a list of items (URI) to a playlist (URI)
+
+import spotipy
+from spotipy.oauth2 import SpotifyOAuth
+
+sp = spotipy.Spotify(auth_manager=SpotifyOAuth(client_id="YOUR_APP_CLIENT_ID",
+                                               
client_secret="YOUR_APP_CLIENT_SECRET",
+                                               
redirect_uri="YOUR_APP_REDIRECT_URI",
+                                               scope="playlist-modify-private"
+                                               ))
+
+sp.playlist_add_items('playlist_id', ['list_of_items'])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/playlist_tracks.py 
new/spotipy-2.23.0/examples/playlist_tracks.py
--- old/spotipy-2.22.0/examples/playlist_tracks.py      2022-12-10 
16:31:47.000000000 +0100
+++ new/spotipy-2.23.0/examples/playlist_tracks.py      2023-04-07 
19:36:17.000000000 +0200
@@ -12,10 +12,10 @@
                                  offset=offset,
                                  fields='items.track.id,total',
                                  additional_types=['track'])
-    
+
     if len(response['items']) == 0:
         break
-    
+
     pprint(response['items'])
     offset = offset + len(response['items'])
-    print(offset, "/", response['total'])
\ No newline at end of file
+    print(offset, "/", response['total'])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/show_artist_top_tracks.py 
new/spotipy-2.23.0/examples/show_artist_top_tracks.py
--- old/spotipy-2.22.0/examples/show_artist_top_tracks.py       2022-12-10 
16:31:47.000000000 +0100
+++ new/spotipy-2.23.0/examples/show_artist_top_tracks.py       2023-04-07 
19:36:17.000000000 +0200
@@ -1,4 +1,5 @@
 # shows artist info for a URN or URL
+# scope is not required for this function
 
 from spotipy.oauth2 import SpotifyClientCredentials
 import spotipy
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/simple0.py 
new/spotipy-2.23.0/examples/simple0.py
--- old/spotipy-2.22.0/examples/simple0.py      2022-12-10 16:31:47.000000000 
+0100
+++ new/spotipy-2.23.0/examples/simple0.py      1970-01-01 01:00:00.000000000 
+0100
@@ -1,9 +0,0 @@
-from spotipy.oauth2 import SpotifyClientCredentials
-import spotipy
-
-client_credentials_manager = SpotifyClientCredentials()
-sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
-
-results = sp.search(q='weezer', limit=20)
-for i, t in enumerate(results['tracks']['items']):
-    print(' ', i, t['name'])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/simple1.py 
new/spotipy-2.23.0/examples/simple1.py
--- old/spotipy-2.22.0/examples/simple1.py      2022-12-10 16:31:47.000000000 
+0100
+++ new/spotipy-2.23.0/examples/simple1.py      1970-01-01 01:00:00.000000000 
+0100
@@ -1,16 +0,0 @@
-from spotipy.oauth2 import SpotifyClientCredentials
-import spotipy
-
-birdy_uri = 'spotify:artist:2WX2uTcsvV5OnS0inACecP'
-
-client_credentials_manager = SpotifyClientCredentials()
-sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
-
-results = sp.artist_albums(birdy_uri, album_type='album')
-albums = results['items']
-while results['next']:
-    results = sp.next(results)
-    albums.extend(results['items'])
-
-for album in albums:
-    print((album['name']))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/simple2.py 
new/spotipy-2.23.0/examples/simple2.py
--- old/spotipy-2.22.0/examples/simple2.py      2022-12-10 16:31:47.000000000 
+0100
+++ new/spotipy-2.23.0/examples/simple2.py      1970-01-01 01:00:00.000000000 
+0100
@@ -1,15 +0,0 @@
-
-from spotipy.oauth2 import SpotifyClientCredentials
-import spotipy
-
-lz_uri = 'spotify:artist:36QJpDe2go2KgaRleHCDTp'
-
-client_credentials_manager = SpotifyClientCredentials()
-sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
-
-results = sp.artist_top_tracks(lz_uri)
-
-for track in results['tracks'][:10]:
-    print('track    : ' + track['name'])
-    print('audio    : ' + track['preview_url'])
-    print('cover art: ' + track['album']['images'][0]['url'])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/simple3.py 
new/spotipy-2.23.0/examples/simple3.py
--- old/spotipy-2.22.0/examples/simple3.py      2022-12-10 16:31:47.000000000 
+0100
+++ new/spotipy-2.23.0/examples/simple3.py      1970-01-01 01:00:00.000000000 
+0100
@@ -1,18 +0,0 @@
-#Shows the name of the artist/band and their image by giving a link
-import sys
-
-from spotipy.oauth2 import SpotifyClientCredentials
-import spotipy
-
-sp = spotipy.Spotify(client_credentials_manager=SpotifyClientCredentials())
-
-if len(sys.argv) > 1:
-    name = ' '.join(sys.argv[1:])
-else:
-    name = 'Radiohead'
-
-results = sp.search(q='artist:' + name, type='artist')
-items = results['artists']['items']
-if len(items) > 0:
-    artist = items[0]
-    print(artist['name'], artist['images'][0]['url'])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/simple4.py 
new/spotipy-2.23.0/examples/simple4.py
--- old/spotipy-2.22.0/examples/simple4.py      2022-12-10 16:31:47.000000000 
+0100
+++ new/spotipy-2.23.0/examples/simple4.py      1970-01-01 01:00:00.000000000 
+0100
@@ -1,12 +0,0 @@
-import spotipy
-from pprint import pprint
-
-
-def main():
-    spotify = spotipy.Spotify(auth_manager=spotipy.SpotifyOAuth())
-    me = spotify.me()
-    pprint(me)
-
-
-if __name__ == "__main__":
-    main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/simple_artist_albums.py 
new/spotipy-2.23.0/examples/simple_artist_albums.py
--- old/spotipy-2.22.0/examples/simple_artist_albums.py 1970-01-01 
01:00:00.000000000 +0100
+++ new/spotipy-2.23.0/examples/simple_artist_albums.py 2023-04-07 
19:36:17.000000000 +0200
@@ -0,0 +1,16 @@
+from spotipy.oauth2 import SpotifyClientCredentials
+import spotipy
+
+birdy_uri = 'spotify:artist:2WX2uTcsvV5OnS0inACecP'
+
+client_credentials_manager = SpotifyClientCredentials()
+sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
+
+results = sp.artist_albums(birdy_uri, album_type='album')
+albums = results['items']
+while results['next']:
+    results = sp.next(results)
+    albums.extend(results['items'])
+
+for album in albums:
+    print((album['name']))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/simple_artist_top_tracks.py 
new/spotipy-2.23.0/examples/simple_artist_top_tracks.py
--- old/spotipy-2.22.0/examples/simple_artist_top_tracks.py     1970-01-01 
01:00:00.000000000 +0100
+++ new/spotipy-2.23.0/examples/simple_artist_top_tracks.py     2023-04-07 
19:36:17.000000000 +0200
@@ -0,0 +1,15 @@
+
+from spotipy.oauth2 import SpotifyClientCredentials
+import spotipy
+
+lz_uri = 'spotify:artist:36QJpDe2go2KgaRleHCDTp'
+
+client_credentials_manager = SpotifyClientCredentials()
+sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
+
+results = sp.artist_top_tracks(lz_uri)
+
+for track in results['tracks'][:10]:
+    print('track    : ' + track['name'])
+    print('audio    : ' + track['preview_url'])
+    print('cover art: ' + track['album']['images'][0]['url'])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/simple_me.py 
new/spotipy-2.23.0/examples/simple_me.py
--- old/spotipy-2.22.0/examples/simple_me.py    1970-01-01 01:00:00.000000000 
+0100
+++ new/spotipy-2.23.0/examples/simple_me.py    2023-04-07 19:36:17.000000000 
+0200
@@ -0,0 +1,12 @@
+import spotipy
+from pprint import pprint
+
+
+def main():
+    spotify = spotipy.Spotify(auth_manager=spotipy.SpotifyOAuth())
+    me = spotify.me()
+    pprint(me)
+
+
+if __name__ == "__main__":
+    main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/simple_search_artist.py 
new/spotipy-2.23.0/examples/simple_search_artist.py
--- old/spotipy-2.22.0/examples/simple_search_artist.py 1970-01-01 
01:00:00.000000000 +0100
+++ new/spotipy-2.23.0/examples/simple_search_artist.py 2023-04-07 
19:36:17.000000000 +0200
@@ -0,0 +1,9 @@
+from spotipy.oauth2 import SpotifyClientCredentials
+import spotipy
+
+client_credentials_manager = SpotifyClientCredentials()
+sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
+
+results = sp.search(q='weezer', limit=20)
+for i, t in enumerate(results['tracks']['items']):
+    print(' ', i, t['name'])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spotipy-2.22.0/examples/simple_search_artist_image_url.py 
new/spotipy-2.23.0/examples/simple_search_artist_image_url.py
--- old/spotipy-2.22.0/examples/simple_search_artist_image_url.py       
1970-01-01 01:00:00.000000000 +0100
+++ new/spotipy-2.23.0/examples/simple_search_artist_image_url.py       
2023-04-07 19:36:17.000000000 +0200
@@ -0,0 +1,18 @@
+# Shows the name of the artist/band and their image by giving a link
+import sys
+
+from spotipy.oauth2 import SpotifyClientCredentials
+import spotipy
+
+sp = spotipy.Spotify(client_credentials_manager=SpotifyClientCredentials())
+
+if len(sys.argv) > 1:
+    name = ' '.join(sys.argv[1:])
+else:
+    name = 'Radiohead'
+
+results = sp.search(q='artist:' + name, type='artist')
+items = results['artists']['items']
+if len(items) > 0:
+    artist = items[0]
+    print(artist['name'], artist['images'][0]['url'])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/examples/user_playlists.py 
new/spotipy-2.23.0/examples/user_playlists.py
--- old/spotipy-2.22.0/examples/user_playlists.py       2022-12-10 
16:31:47.000000000 +0100
+++ new/spotipy-2.23.0/examples/user_playlists.py       2023-04-07 
19:36:17.000000000 +0200
@@ -16,4 +16,4 @@
 playlists = sp.user_playlists(username)
 
 for playlist in playlists['items']:
-    print(playlist['name'])
\ No newline at end of file
+    print(playlist['name'])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/setup.py new/spotipy-2.23.0/setup.py
--- old/spotipy-2.22.0/setup.py 2022-12-10 16:31:47.000000000 +0100
+++ new/spotipy-2.23.0/setup.py 2023-04-07 19:36:17.000000000 +0200
@@ -18,7 +18,7 @@
 
 setup(
     name='spotipy',
-    version='2.22.0',
+    version='2.23.0',
     description='A light weight Python library for the Spotify Web API',
     long_description=long_description,
     long_description_content_type="text/markdown",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/spotipy/cache_handler.py 
new/spotipy-2.23.0/spotipy/cache_handler.py
--- old/spotipy-2.22.0/spotipy/cache_handler.py 2022-12-10 16:31:47.000000000 
+0100
+++ new/spotipy-2.23.0/spotipy/cache_handler.py 2023-04-07 19:36:17.000000000 
+0200
@@ -50,15 +50,18 @@
 
     def __init__(self,
                  cache_path=None,
-                 username=None):
+                 username=None,
+                 encoder_cls=None):
         """
         Parameters:
              * cache_path: May be supplied, will otherwise be generated
                            (takes precedence over `username`)
              * username: May be supplied or set as environment variable
                          (will set `cache_path` to `.cache-{username}`)
+             * encoder_cls: May be supplied as a means of overwriting the
+                        default serializer used for writing tokens to disk
         """
-
+        self.encoder_cls = encoder_cls
         if cache_path:
             self.cache_path = cache_path
         else:
@@ -88,7 +91,7 @@
     def save_token_to_cache(self, token_info):
         try:
             f = open(self.cache_path, "w")
-            f.write(json.dumps(token_info))
+            f.write(json.dumps(token_info, cls=self.encoder_cls))
             f.close()
         except IOError:
             logger.warning('Couldn\'t write token to cache at: %s',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/spotipy/client.py 
new/spotipy-2.23.0/spotipy/client.py
--- old/spotipy-2.22.0/spotipy/client.py        2022-12-10 16:31:47.000000000 
+0100
+++ new/spotipy-2.23.0/spotipy/client.py        2023-04-07 19:36:17.000000000 
+0200
@@ -6,6 +6,7 @@
 
 import json
 import logging
+import re
 import warnings
 
 import requests
@@ -14,6 +15,8 @@
 
 from spotipy.exceptions import SpotifyException
 
+from collections import defaultdict
+
 logger = logging.getLogger(__name__)
 
 
@@ -96,6 +99,29 @@
         "US",
         "UY"]
 
+    # Spotify URI scheme defined in [1], and the ID format as base-62 in [2].
+    #
+    # Unfortunately the IANA specification is out of date and doesn't include 
the new types
+    # show and episode. Additionally, for the user URI, it does not specify 
which characters
+    # are valid for usernames, so the assumption is alphanumeric which 
coincidentially are also
+    # the same ones base-62 uses.
+    # In limited manual exploration this seems to hold true, as newly accounts 
are assigned an
+    # identifier that looks like the base-62 of all other IDs, but some older 
accounts only have
+    # numbers and even older ones seemed to have been allowed to freely pick 
this name.
+    #
+    # [1] https://www.iana.org/assignments/uri-schemes/prov/spotify
+    # [2] 
https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids
+    _regex_spotify_uri = 
r'^spotify:(?:(?P<type>track|artist|album|playlist|show|episode):(?P<id>[0-9A-Za-z]+)|user:(?P<username>[0-9A-Za-z]+):playlist:(?P<playlistid>[0-9A-Za-z]+))$'
  # noqa: E501
+
+    # Spotify URLs are defined at [1]. The assumption is made that they are all
+    # pointing to open.spotify.com, so a regex is used to parse them as well,
+    # instead of a more complex URL parsing function.
+    #
+    # [1] 
https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids
+    _regex_spotify_url = 
r'^(http[s]?:\/\/)?open.spotify.com\/(?P<type>track|artist|album|playlist|show|episode|user)\/(?P<id>[0-9A-Za-z]+)(\?.*)?$'
  # noqa: E501
+
+    _regex_base62 = r'^[0-9A-Za-z]+$'
+
     def __init__(
         self,
         auth=None,
@@ -570,12 +596,12 @@
                       official documentation 
https://developer.spotify.com/documentation/web-api/reference/search/)  # noqa
                 - limit  - the number of items to return (min = 1, default = 
10, max = 50). If a search is to be done on multiple
                             markets, then this limit is applied to each 
market. (e.g. search US, CA, MX each with a limit of 10).
+                            If multiple types are specified, this applies to 
each type.
                 - offset - the index of the first item to return
                 - type - the types of items to return. One or more of 
'artist', 'album',
                          'track', 'playlist', 'show', or 'episode'. If 
multiple types are desired, pass in a comma separated string.
                 - markets - A list of ISO 3166-1 alpha-2 country codes. Search 
all country markets by default.
-                - total - the total number of results to return if multiple 
markets are supplied in the search.
-                          If multiple types are specified, this only applies 
to the first type.
+                - total - the total number of results to return across 
multiple markets and types.
         """
         warnings.warn(
             "Searching multiple markets is an experimental feature. "
@@ -846,8 +872,27 @@
                 - tracks - a list of track URIs, URLs or IDs
                 - position - the position to add the tracks
         """
+        tracks = [self._get_uri("track", tid) for tid in tracks]
         return self.playlist_add_items(playlist_id, tracks, position)
 
+    def user_playlist_add_episodes(
+        self, user, playlist_id, episodes, position=None
+    ):
+        warnings.warn(
+            "You should use `playlist_add_items(playlist_id, episodes)` 
instead",
+            DeprecationWarning,
+        )
+        """ Adds episodes to a playlist
+
+            Parameters:
+                - user - the id of the user
+                - playlist_id - the id of the playlist
+                - episodes - a list of track URIs, URLs or IDs
+                - position - the position to add the episodes
+        """
+        episodes = [self._get_uri("episode", tid) for tid in episodes]
+        return self.playlist_add_items(playlist_id, episodes, position)
+
     def user_playlist_replace_tracks(self, user, playlist_id, tracks):
         """ Replace all tracks in a playlist for a user
 
@@ -1032,7 +1077,7 @@
 
             Parameters:
                 - playlist_id - the id of the playlist
-                - items - a list of track/episode URIs, URLs or IDs
+                - items - a list of track/episode URIs or URLs
                 - position - the position to add the tracks
         """
         plid = self._get_id("playlist", playlist_id)
@@ -1739,7 +1784,7 @@
     ):
         """ Start or resume user's playback.
 
-            Provide a `context_uri` to start playback or an album,
+            Provide a `context_uri` to start playback of an album,
             artist, or playlist.
 
             Provide a `uris` list to start playback of one or more
@@ -1921,20 +1966,28 @@
         return path
 
     def _get_id(self, type, id):
-        fields = id.split(":")
-        if len(fields) >= 3:
-            if type != fields[-2]:
-                logger.warning('Expected id of type %s but found type %s %s',
-                               type, fields[-2], id)
-            return fields[-1].split("?")[0]
-        fields = id.split("/")
-        if len(fields) >= 3:
-            itype = fields[-2]
-            if type != itype:
-                logger.warning('Expected id of type %s but found type %s %s',
-                               type, itype, id)
-            return fields[-1].split("?")[0]
-        return id
+        uri_match = re.search(Spotify._regex_spotify_uri, id)
+        if uri_match is not None:
+            uri_match_groups = uri_match.groupdict()
+            if uri_match_groups['type'] != type:
+                # TODO change to a ValueError in v3
+                raise SpotifyException(400, -1, "Unexpected Spotify URI type.")
+            return uri_match_groups['id']
+
+        url_match = re.search(Spotify._regex_spotify_url, id)
+        if url_match is not None:
+            url_match_groups = url_match.groupdict()
+            if url_match_groups['type'] != type:
+                raise SpotifyException(400, -1, "Unexpected Spotify URL type.")
+            # TODO change to a ValueError in v3
+            return url_match_groups['id']
+
+        # Raw identifiers might be passed, ensure they are also base-62
+        if re.search(Spotify._regex_base62, id) is not None:
+            return id
+
+        # TODO change to a ValueError in v3
+        raise SpotifyException(400, -1, "Unsupported URL / URI.")
 
     def _get_uri(self, type, id):
         if self._is_uri(id):
@@ -1943,7 +1996,7 @@
             return "spotify:" + type + ":" + self._get_id(type, id)
 
     def _is_uri(self, uri):
-        return uri.startswith("spotify:") and len(uri.split(':')) == 3
+        return re.search(Spotify._regex_spotify_uri, uri) is not None
 
     def _search_multiple_markets(self, q, limit, offset, type, markets, total):
         if total and limit > total:
@@ -1954,22 +2007,29 @@
                 UserWarning,
             )
 
-        results = {}
-        first_type = type.split(",")[0] + 's'
+        results = defaultdict(dict)
+        item_types = [item_type + "s" for item_type in type.split(",")]
         count = 0
 
         for country in markets:
             result = self._get(
                 "search", q=q, limit=limit, offset=offset, type=type, 
market=country
             )
-            results[country] = result
+            for item_type in item_types:
+                results[country][item_type] = result[item_type]
+
+                # Truncate the items list to the current limit
+                if len(results[country][item_type]['items']) > limit:
+                    results[country][item_type]['items'] = \
+                        results[country][item_type]['items'][:limit]
+
+                count += len(results[country][item_type]['items'])
+                if total and limit > total - count:
+                    # when approaching `total` results, adjust `limit` to not 
request more
+                    # items than needed
+                    limit = total - count
 
-            count += len(result[first_type]['items'])
             if total and count >= total:
-                break
-            if total and limit > total - count:
-                # when approaching `total` results, adjust `limit` to not 
request more
-                # items than needed
-                limit = total - count
+                return results
 
         return results
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spotipy-2.22.0/spotipy/oauth2.py 
new/spotipy-2.23.0/spotipy/oauth2.py
--- old/spotipy-2.22.0/spotipy/oauth2.py        2022-12-10 16:31:47.000000000 
+0100
+++ new/spotipy-2.23.0/spotipy/oauth2.py        2023-04-07 19:36:17.000000000 
+0200
@@ -629,7 +629,7 @@
     """ Implements PKCE Authorization Flow for client apps
 
     This auth manager enables *user and non-user* endpoints with only
-    a client secret, redirect uri, and username. When the app requests
+    a client ID, redirect URI, and username. When the app requests
     an access token for the first time, the user is prompted to
     authorize the new client app. After authorizing the app, the client
     app is then given both access and refresh tokens. This is the
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spotipy-2.22.0/tests/integration/non_user_endpoints/test.py 
new/spotipy-2.23.0/tests/integration/non_user_endpoints/test.py
--- old/spotipy-2.22.0/tests/integration/non_user_endpoints/test.py     
2022-12-10 16:31:47.000000000 +0100
+++ new/spotipy-2.23.0/tests/integration/non_user_endpoints/test.py     
2023-04-07 19:36:17.000000000 +0200
@@ -221,6 +221,87 @@
             total_limited_results += 
len(results_limited[country]['artists']['items'])
         self.assertTrue(total_limited_results <= total)
 
+    def test_multiple_types_search_with_multiple_markets(self):
+        total = 14
+
+        countries_list = ['GB', 'US', 'AU']
+        countries_tuple = ('GB', 'US', 'AU')
+
+        results_multiple = self.spotify.search_markets(q='abba', 
type='artist,track',
+                                                       markets=countries_list)
+        results_all = self.spotify.search_markets(q='abba', 
type='artist,track')
+        results_tuple = self.spotify.search_markets(q='abba', 
type='artist,track',
+                                                    markets=countries_tuple)
+        results_limited = self.spotify.search_markets(q='abba', limit=3, 
type='artist,track',
+                                                      markets=countries_list, 
total=total)
+
+        # Asserts 'artists' property is present in all responses
+        self.assertTrue(
+            all('artists' in results_multiple[country] for country in 
results_multiple))
+        self.assertTrue(all('artists' in results_all[country] for country in 
results_all))
+        self.assertTrue(all('artists' in results_tuple[country] for country in 
results_tuple))
+        self.assertTrue(all('artists' in results_limited[country] for country 
in results_limited))
+
+        # Asserts 'tracks' property is present in all responses
+        self.assertTrue(
+            all('tracks' in results_multiple[country] for country in 
results_multiple))
+        self.assertTrue(all('tracks' in results_all[country] for country in 
results_all))
+        self.assertTrue(all('tracks' in results_tuple[country] for country in 
results_tuple))
+        self.assertTrue(all('tracks' in results_limited[country] for country 
in results_limited))
+
+        # Asserts 'artists' list is nonempty in unlimited searches
+        self.assertTrue(
+            all(len(results_multiple[country]['artists']['items']) > 0 for 
country in
+                results_multiple))
+        self.assertTrue(all(len(results_all[country]['artists']
+                        ['items']) > 0 for country in results_all))
+        self.assertTrue(
+            all(len(results_tuple[country]['artists']['items']) > 0 for 
country in results_tuple))
+
+        # Asserts 'tracks' list is nonempty in unlimited searches
+        self.assertTrue(
+            all(len(results_multiple[country]['tracks']['items']) > 0 for 
country in
+                results_multiple))
+        self.assertTrue(all(len(results_all[country]['tracks']
+                        ['items']) > 0 for country in results_all))
+        self.assertTrue(all(len(results_tuple[country]['tracks']
+                        ['items']) > 0 for country in results_tuple))
+
+        # Asserts artist name is the first artist result in all searches
+        self.assertTrue(all(results_multiple[country]['artists']['items']
+                            [0]['name'] == 'ABBA' for country in 
results_multiple))
+        self.assertTrue(all(results_all[country]['artists']['items']
+                            [0]['name'] == 'ABBA' for country in results_all))
+        self.assertTrue(all(results_tuple[country]['artists']['items']
+                            [0]['name'] == 'ABBA' for country in 
results_tuple))
+        self.assertTrue(all(results_limited[country]['artists']['items']
+                            [0]['name'] == 'ABBA' for country in 
results_limited))
+
+        # Asserts track name is present in responses from specified markets
+        self.assertTrue(all('Dancing Queen' in
+                            [item['name'] for item in 
results_multiple[country]['tracks']['items']]
+                            for country in results_multiple))
+        self.assertTrue(all('Dancing Queen' in
+                            [item['name'] for item in 
results_tuple[country]['tracks']['items']]
+                            for country in results_tuple))
+
+        # Asserts expected number of items are returned based on the total
+        # 3 artists + 3 tracks = 6 items returned from first market
+        # 3 artists + 3 tracks = 6 items returned from second market
+        # 2 artists + 0 tracks = 2 items returned from third market
+        # 14 items returned total
+        self.assertEqual(len(results_limited['GB']['artists']['items']), 3)
+        self.assertEqual(len(results_limited['GB']['tracks']['items']), 3)
+        self.assertEqual(len(results_limited['US']['artists']['items']), 3)
+        self.assertEqual(len(results_limited['US']['tracks']['items']), 3)
+        self.assertEqual(len(results_limited['AU']['artists']['items']), 2)
+        self.assertEqual(len(results_limited['AU']['tracks']['items']), 0)
+
+        item_count = sum([len(market_result['artists']['items']) + 
len(market_result['tracks']
+                         ['items']) for market_result in 
results_limited.values()])
+
+        self.assertEqual(item_count, total)
+
     def test_artist_albums(self):
         results = self.spotify.artist_albums(self.weezer_urn)
         self.assertTrue('items' in results)

Reply via email to