spotipy-dev / spotipy

A light weight Python library for the Spotify Web API
http://spotipy.readthedocs.org
MIT License
4.97k stars 956 forks source link

warning:couldn't write token cache to .cache- #870

Open F-Wer opened 1 year ago

F-Wer commented 1 year ago

Describe the bug I try to run spotipy via a scheduled job on Windows 10.

Therefore I created a .bat file that calls the script. When I run the script normally it works, but if I try to run it via a job or via the .bat file I get a popup of the command prompt with a url I have to go and insert the url I got redirected to. Afterwars I get the error

Couldn't write token to cache at: .cache-XXXXXX

.bat file

"C:\Users\F\AppData\Local\Programs\Python\Spotify_Playlist_followed_artists\Scripts\python.exe" -i "C:\Users\F\PycharmProjects\Spotify_Playlist_followed_artists\main.py"
pause

In my Script it throws the error in my method where I initialize Spotipy:

def get_env():
    load_dotenv()
    SPOTIPY_CLIENT_ID = os.environ.get("SPOTIPY_CLIENT_ID")
    SPOTIPY_CLIENT_SECRET = os.environ.get("SPOTIPY_CLIENT_SECRET")
    SPOTIPY_REDIRECT_URI = os.environ.get("SPOTIPY_REDIRECT_URI")
    SPOTIPY_USER_ID = os.environ.get("SPOTIPY_USER_ID")
    scope = "user-follow-read,playlist-modify-private,playlist-modify-public"
    client_credentials_manager = spotipy.oauth2.SpotifyOAuth(
        scope=scope,
        username=SPOTIPY_USER_ID,
        client_id=SPOTIPY_CLIENT_ID,
        client_secret=SPOTIPY_CLIENT_SECRET,
        redirect_uri=SPOTIPY_REDIRECT_URI,
        open_browser=False)
    SP = spotipy.Spotify(client_credentials_manager=client_credentials_manager)

Expected behavior I expect to enter the return url and that the .cache file could be written to.

Output Go to the following URL: https://accounts.spotify.com/authorize?client_id=XXXXX&response_type=code&redirect_uri=http%3A%2F%XXXXX%3A8080&scope=playlist-modify-private+playlist-modify-public+user-follow-read Enter the URL you were redirected to: http://XXXXXX/?code=XXXX-YYYY-ZZZZ-DDDD-WWWW Couldn't write token to cache at: .cache-XXXXXX

Environment:

Additional context I know that there is already an issue #363 , but the workarounds in there didn't work for me

Peter-Schorn commented 1 year ago

Given that the script works when run directly, but not when run from the .bat file, this sounds like a permissions issue, just like in #363. You should modify this method to print the error received for more information. https://github.com/plamere/spotipy/blob/be2739146138a0d5717967cc62927c15ecc9a7c3/spotipy/cache_handler.py#L88-L95

SergiuPogor commented 1 year ago

I was getting a similar error on Linux, after enable logging the error detail I got this one: [Errno 21] Is a directory: '.cache' So, in my case, the script was looking for the file in "/.cache" I've fixed the issue just by adding absolute file path cache_path=PROJECT_PATH + '/.cache'

sp = SpotifyOAuth(client_id=config.SPOTIFY_APP_CLIENT_ID, client_secret=config.SPOTIFY_APP_CLIENT_SECRET, redirect_uri=config.SPOTIFY_APP_REDIRECT_URL, cache_path=PROJECT_PATH + '/.cache')

rebecca-riley commented 1 year ago

Same issue, same error. When I run my script on the command prompt directly, it works fine. When I use the Windows launcher, "couldn't write token to cache at: .cache-username." Error is originating from call spotipy.Spotify(auth=token), where token is similar to client_credentials_manager above.

Modified the save_token_to_cache as suggested by @Peter-Schorn and it does appear to be a permissions issue: [Errno 13] Permission denied: '.cache-username'

I'll report back if I find a fix.

p.s. I also tried the fixes suggested on #363 -- including Windows Defender whitelisting, interactive mode, and changing cache_path -- with no success. I think it's important to note that the dotfiles themselves aren't inherently problematic. They are created and utilized just fine on direct execution. It's the execution method that seems to be the source of the problem.

Also of note: I initially built/tested this script on Linux and am now modifying for Windows compatibility. I never had this problem on Linux.

Peter-Schorn commented 1 year ago

It's the execution method that seems to be the source of the problem.

How do you suggest we change it? The parent process needs to have write permission to the cache path. spotipy can't change that requirement. You must invoke the script with the proper permissions.

rebecca-riley commented 1 year ago

Source of the problem

The permissions issue is arising when using the launcher because cache_handler.py by default attempts to create a file in the current directory, i.e. the launcher directory, but it is protected in Windows (e.g. C:\WINDOWS\system32 in my case). This can be fixed by specifying a full cache_path in the authorization call; see below. (Note that this is not the cache_path fix suggested in #363, which just amounted to removing the dot.)

When executing a script directly from Windows command prompt, this will always (?) be done from a directory with read-write permissions, so no error.

Minimal example

import os
import errno

def main():
    print('Program started.')
    print('Current directory: ' + os.getcwd())

    try:
        # f = open(".cache-test", "w")  # problem line -- no error with direct execution in command prompt, error when using launcher
        # f = open(str(os.path.dirname(os.path.abspath(__file__))) + '\\' + '.cache-test', "w")  # solution line -- works with both direct execution and launcher
        f.write('test')
        f.close()
        print('Success.')
    except IOError as e:
        print(e)

    input('Press enter to exit.')

if __name__ == '__main__':
    main()

Uncommenting the problem line produces the error in question: [Errno 13] Permission denied: '.cache-test' when using the launcher. Uncommenting the solution line successfully writes .cache-test to the directory containing the test script. Both lines work on direct execution.

Solution using Spotipy

token = spotipy.util.prompt_for_user_token(username,scope,
 client_id=id,
 client_secret=secret,
 redirect_uri='http://localhost:8888/callback',
 cache_path=str(os.path.dirname(os.path.abspath(__file__))) + '\\' + '.cache-' + username)

spotify = spotipy.Spotify(auth=token)

Specifying a full cache_path to a writeable directory (in this case, the directory in which the script is located) resolves the issue. This fix must also be included anywhere else a file is opened in the script. (Otherwise you will get the same permission error with each open() call.)

rebecca-riley commented 1 year ago

@Peter-Schorn I agree with you -- this is a user/permissions issue, not actually a Spotipy issue. I don't think a change is necessary. It would be clunky and out of keeping with the rest of Spotipy and would probably lead to more unexpected behavior than helpful behavior.

The error is very specific. Hopefully this solution has enough keywords for people to find it upon Googling their similar problem.

You might consider including a warning about using the Windows Python launcher in the documentation and/or providing a more descriptive error message upon encountering a permissions error. It's not such a strange problem, but it was initially not at all clear that permissions were the reason for the failure.

Peter-Schorn commented 1 year ago

I agree the error message should be displayed in the log message. I'm sure @stephanebruckert would welcome a PR with that change.

rebecca-riley commented 1 year ago

Sure thing, I'll add one soon.