Notebooks hebben geen terminal, maar jouw OAuth-bibliotheek gaat er wel van uit
De foutmelding is direct:
StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.Dit treedt op wanneer een Python-bibliotheek input() (of raw_input() in Python 2) aanroept om de gebruiker iets te vragen: een URL om terug te plakken, een bevestiging, een code. Lokaal in een terminal werkt dat prima. In een Databricks notebook is er geen stdin file descriptor gekoppeld aan het kernel-proces. De IPython-kernel die Databricks notebooks aanstuurt, gooit expliciet een StdinNotImplementedError in plaats van eindeloos te wachten op een read die nooit voltooid wordt.
De Spotify API is een veelvoorkomende trigger, maar niet de enige. Elke OAuth 2.0 Authorization Code flow die een lokale redirect gebruikt en de gebruiker vervolgens vraagt een URL terug te plakken, loopt hier tegenaan. De SpotifyOAuth.get_access_token() methode van de spotipy-bibliotheek valt, wanneer ze geen browser kan openen en geen gecachte token heeft, terug op een input() aanroep die om de redirect URL vraagt. Die ene regel brengt de notebook cell tot stilstand.
Het is belangrijk te begrijpen: dit is geen bug in spotipy of in Databricks. Het is een architectureel mismatch. Authorization Code flow is ontworpen voor applicaties waarbij een gebruiker achter een browser zit. Databricks notebooks zijn headless compute-omgevingen die toevallig een web-UI hebben voor het bewerken van code. De notebook-UI proxyt stdin niet naar de kernel. Geen enkele configuratie verandert dat. Je hebt een andere authenticatiestrategie nodig.
Client Credentials flow elimineert de prompt volledig
Als jouw workload geen gebruikersspecifieke data nodig heeft (publieke playlists, trackmetadata, audiofuncties of cataloguszoekopdrachten), schakel dan over van SpotifyOAuth naar SpotifyClientCredentials. Dit is de meest voorkomende oplossing voor deze fout.
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
auth_manager = SpotifyClientCredentials(
client_id=dbutils.secrets.get(scope="spotify", key="client_id"),
client_secret=dbutils.secrets.get(scope="spotify", key="client_secret")
)
sp = spotipy.Spotify(auth_manager=auth_manager)
# Dit werkt — geen input() aanroep, geen browser redirect
results = sp.search(q="artist:Radiohead", type="track", limit=10)Client Credentials flow is een directe POST naar https://accounts.spotify.com/api/token met je client ID en secret. Spotify geeft een access token terug dat 3600 seconden geldig is. Geen toestemmingsscherm, geen redirect URI, geen stdin. De spotipy-bibliotheek regelt het vernieuwen automatisch.
Twee dingen om op te letten. Bewaar je credentials in Databricks secrets (dbutils.secrets), niet in notebook cells. Hardcoded secrets in notebooks komen terecht in versiebeheer, worden gelogd in job output en zijn zichtbaar in de Databricks workspace voor iedereen met leestoegang. Client Credentials tokens kunnen bovendien geen toegang krijgen tot gebruikers-privé endpoints: opgeslagen nummers, privé playlists, gebruikersprofieldetails en afspeelbesturing. Als je die nodig hebt, lees dan de volgende sectie.
Pre-autoriseer tokens buiten Databricks en injecteer ze
Wanneer je echt user-scoped toegang nodig hebt (de opgeslagen bibliotheek van een gebruiker ophalen, privé playlists lezen of namens hem naar een playlist schrijven), heb je een Authorization Code token nodig. Maar die flow kun je niet binnen Databricks uitvoeren. De oplossing is om token-acquisitie van token-gebruik te scheiden.
Stap 1: Voer de OAuth flow lokaal uit. Op je laptop, voer de autorisatie eenmalig uit:
from spotipy.oauth2 import SpotifyOAuth
sp_oauth = SpotifyOAuth(
client_id="YOUR_CLIENT_ID",
client_secret="YOUR_CLIENT_SECRET",
redirect_uri="http://localhost:8080/callback",
scope="user-library-read playlist-read-private",
cache_path=".spotify_token_cache"
)
token_info = sp_oauth.get_access_token(as_dict=True)Dit opent je browser, je geeft toestemming en spotipy schrijft het token (inclusief het refresh token) naar .spotify_token_cache.
Stap 2: Sla het refresh token op in Databricks secrets:
databricks secrets put-secret spotify refresh_token --string-value "AQD..."Stap 3: Bouw in je Databricks notebook een token refresh-mechanisme dat input() nooit aanroept:
import requests
def refresh_spotify_token():
refresh_token = dbutils.secrets.get(scope="spotify", key="refresh_token")
client_id = dbutils.secrets.get(scope="spotify", key="client_id")
client_secret = dbutils.secrets.get(scope="spotify", key="client_secret")
response = requests.post(
"https://accounts.spotify.com/api/token",
data={
"grant_type": "refresh_token",
"refresh_token": refresh_token,
"client_id": client_id,
"client_secret": client_secret
}
)
return response.json()["access_token"]
sp = spotipy.Spotify(auth=refresh_spotify_token())Dit patroon werkt voor elke OAuth-provider, niet alleen Spotify. De localhost redirect URI die de oorspronkelijke communitygebruiker gebruikte, is prima voor de lokale stap. Die kan alleen niet binnen Databricks worden uitgevoerd.
De MemoryCacheHandler als omweg, specifiek voor spotipy
De architectuur van spotipy heeft een elegantere uitweg dan directe requests.post aanroepen: de CacheHandler interface. In plaats van spotipy te laten proberen .cache bestanden te lezen en schrijven (wat ook permissieproblemen veroorzaakt op DBFS), injecteer je een MemoryCacheHandler die vooraf geladen is met jouw token-informatie.
from spotipy.oauth2 import SpotifyOAuth
from spotipy.cache_handler import MemoryCacheHandler
token_info = {
"access_token": "current_access_token_here",
"refresh_token": dbutils.secrets.get(scope="spotify", key="refresh_token"),
"token_type": "Bearer",
"expires_in": 3600,
"scope": "user-library-read playlist-read-private",
"expires_at": 0 # Dwingt directe refresh
}
cache_handler = MemoryCacheHandler(token_info=token_info)
auth_manager = SpotifyOAuth(
client_id=dbutils.secrets.get(scope="spotify", key="client_id"),
client_secret=dbutils.secrets.get(scope="spotify", key="client_secret"),
redirect_uri="http://localhost:8080/callback",
scope="user-library-read playlist-read-private",
cache_handler=cache_handler,
open_browser=False
)
sp = spotipy.Spotify(auth_manager=auth_manager)expires_at op 0 zetten dwingt spotipy om het token te vernieuwen via het refresh token bij de eerste API-aanroep. Omdat de refresh grant een server-to-server POST is, triggert hij nooit input(). De open_browser=False vlag voorkomt de fallback browser-poging, die ook stil mislukt in Databricks omdat er geen desktopomgeving is.
Deze aanpak houdt je binnen de beheerde token-lifecycle van spotipy: automatisch vernieuwen, scope-validatie en retry-logica, zonder de StdinNotImplementedError. Het vermijdt ook de bestandssysteemproblemen die CacheFileHandler veroorzaakt op cluster-opslag, waarbij de werkdirectory kan resetten tussen job runs of na cluster-herstarts.
Stille token-verlopen zijn het echte productierisico
De community-thread eindigde zonder bevestigde oplossing, wat typisch is. Iemand krijgt de notebook aan het werk, gaat verder en plant hem als Databricks job. Drie weken later mislukt die job stil.
Hier is waarom. Spotify refresh tokens die onder de Authorization Code flow zijn uitgegeven, verlopen niet op een vast schema, maar ze kunnen worden ingetrokken: wanneer de gebruiker zijn wachtwoord wijzigt, wanneer de API credentials van de app worden geroteerd of wanneer Spotify tokens ongeldig maakt tijdens beveiligingsincidenten. Wanneer dat gebeurt, geeft de refresh-aanroep HTTP 400 terug met {"error": "invalid_grant"}. Je job mislukt. Als je job-uitkomsten niet monitort, stopt de data gewoon met updaten.
Voor Databricks jobs die externe APIs ophalen, is het failure mode geen directe crash maar stale data. De job draait, de token refresh mislukt, de API-aanroep geeft 401 terug en afhankelijk van je foutafhandeling kan de job zelfs succesvol afsluiten met nul geschreven rijen. Downstream tabellen zien er actueel uit omdat de job-tijdstempel wordt bijgewerkt. De data niet.
MetricSign monitort Databricks job-uitkomsten en markeert runs waarbij het outputvolume naar nul daalt of afwijkt van historische patronen. In plaats van drie weken later te ontdekken dat je Spotify pipeline stilletjes is gestopt met data inladen, krijg je een alert bij de eerste afwijkende run met context over wat er is veranderd.
De bredere les geldt ook buiten Spotify. Elke Databricks job die afhankelijk is van externe OAuth tokens (Salesforce, HubSpot, Google Analytics, Stripe) draagt hetzelfde risico. Bouw tokenvalidatie in je pipeline: controleer de HTTP-status van een lichte API-aanroep voordat je de volledige extractie uitvoert en gooi een expliciete exception bij 401. Laat een stille auth failure niet doorgaan als een leeg resultaat.
Andere bibliotheken die dezelfde StdinNotImplementedError gooien
Spotipy is de meest besproken case, maar elk Python-pakket dat input() aanroept tijdens runtime, mislukt op identieke wijze in Databricks. Veelvoorkomende gevallen:
De InstalledAppFlow.run_console() methode van de google-auth-oauthlib bibliotheek vraagt de gebruiker een URL te bezoeken en een autorisatiecode terug te plakken. Gebruik in Databricks google.oauth2.service_account.Credentials.from_service_account_info(), waarbij je de service account JSON laadt uit Databricks secrets.
De OAuth 1.0a flow van de tweepy bibliotheek (OAuthHandler.get_authorization_url() gevolgd door input() voor de verificatie-PIN). Gebruik Bearer Token-authenticatie (tweepy.Client met een bearer token) voor alleen-lezen toegang, of pre-autoriseer en sla tokens op.
Elk aangepast script dat input() gebruikt voor bevestigingsprompts zoals input('Weet je zeker dat je wilt verwijderen? j/n'). Vervang deze door expliciete boolean-parameters of configuratievlaggen bij geautomatiseerde omgevingen.
De oplossing is altijd dezelfde structurele wijziging: scheid de interactieve menselijke stap van de geautomatiseerde compute-stap. Authenticeer waar je een browser en toetsenbord hebt. Voer uit waar je compute en een scheduler hebt. Stuur tokens door, nooit prompts.
Je kunt dit proactief detecteren door je codebase te doorzoeken op input( aanroepen in modules die op Databricks draaien:
import inspect, spotipy
source = inspect.getsource(spotipy.oauth2)
assert 'input(' not in source, 'This module uses input() — will fail in Databricks'Die assertion is simplistisch maar effectief als pre-flight check in de setup-cell van je notebook.