@@ -57,6 +57,38 @@ def _post_form(url, params):
5757 raise LoginError (f"request to { url } failed: { exc } " )
5858
5959
60+ def _post_form_lenient (url , params ):
61+ """POST form params; return (body, status) without raising on HTTP errors.
62+
63+ The token endpoint answers pending/slow_down polls with HTTP errors as
64+ part of the normal flow: callers classify instead of catching.
65+ """
66+ try :
67+ request = urllib .request .Request (
68+ url ,
69+ data = urllib .parse .urlencode (params ).encode (),
70+ headers = {
71+ "Accept" : "application/json" ,
72+ "Content-Type" : "application/x-www-form-urlencoded" ,
73+ "x-api-version" : API_VERSION ,
74+ },
75+ method = "POST" ,
76+ )
77+ with urllib .request .urlopen (request , timeout = REQUEST_TIMEOUT_SECS ) as response :
78+ status = response .status
79+ try :
80+ return json .load (response ), status
81+ except Exception :
82+ return None , status
83+ except urllib .error .HTTPError as exc :
84+ try :
85+ return json .loads (exc .read ().decode ("utf-8" , "replace" )), exc .code
86+ except Exception :
87+ return None , exc .code
88+ except Exception as exc :
89+ raise LoginError (f"request to { url } failed: { exc } " )
90+
91+
6092def _post_json (url , payload , bearer ):
6193 """POST a JSON payload with bearer auth; return the decoded body."""
6294 try :
@@ -102,7 +134,7 @@ def poll_token(device_code, interval_seconds, expires_in_seconds):
102134 deadline = time .time () + float (expires_in_seconds or 600 )
103135 slow_downs = 0
104136 while time .time () < deadline :
105- body , _ = _post_form (
137+ body , _ = _post_form_lenient (
106138 TOKEN_URL ,
107139 {
108140 "grant_type" : "urn:ietf:params:oauth:grant-type:device_code" ,
@@ -111,7 +143,7 @@ def poll_token(device_code, interval_seconds, expires_in_seconds):
111143 },
112144 )
113145 if not isinstance (body , dict ):
114- raise LoginError ( "token endpoint returned an unexpected response")
146+ body = { "error" : "unreadable token response"}
115147 error = body .get ("error" )
116148 if not error :
117149 access = body .get ("access_token" )
0 commit comments