|
5 | 5 | import textwrap
|
6 | 6 | import threading
|
7 | 7 | import warnings
|
| 8 | +from datetime import datetime |
8 | 9 |
|
9 | 10 | import google.auth as gauth
|
10 | 11 | import google.auth.compute_engine
|
|
16 | 17 | from google.oauth2.credentials import Credentials
|
17 | 18 | from google_auth_oauthlib.flow import InstalledAppFlow
|
18 | 19 |
|
| 20 | +from gcsfs.retry import HttpError |
| 21 | + |
19 | 22 | logger = logging.getLogger("gcsfs.credentials")
|
20 | 23 |
|
21 | 24 | tfile = os.path.join(os.path.expanduser("~"), ".gcs_tokens")
|
@@ -99,7 +102,6 @@ def _connect_cloud(self):
|
99 | 102 | raise ValueError("Invalid gcloud credentials") from error
|
100 | 103 |
|
101 | 104 | def _connect_cache(self):
|
102 |
| - |
103 | 105 | if len(self.tokens) == 0:
|
104 | 106 | raise ValueError("No cached tokens")
|
105 | 107 |
|
@@ -167,19 +169,41 @@ def _connect_token(self, token):
|
167 | 169 | if self.credentials.valid:
|
168 | 170 | self.credentials.apply(self.heads)
|
169 | 171 |
|
170 |
| - def maybe_refresh(self): |
171 |
| - # this uses requests and is blocking |
| 172 | + def _credentials_valid(self, refresh_buffer): |
| 173 | + return ( |
| 174 | + self.credentials.valid |
| 175 | + # In addition to checking current validity, we ensure that there is |
| 176 | + # not a near-future expiry to avoid errors when expiration hits. |
| 177 | + and self.credentials.expiry |
| 178 | + and (self.credentials.expiry - datetime.utcnow()).total_seconds() |
| 179 | + > refresh_buffer |
| 180 | + ) |
| 181 | + |
| 182 | + def maybe_refresh(self, refresh_buffer=300): |
| 183 | + """ |
| 184 | + Check and refresh credentials if needed |
| 185 | + """ |
172 | 186 | if self.credentials is None:
|
173 | 187 | return # anon
|
174 |
| - if self.credentials.valid: |
175 |
| - return # still good |
| 188 | + |
| 189 | + if self._credentials_valid(refresh_buffer): |
| 190 | + return # still good, with buffer |
| 191 | + |
176 | 192 | with requests.Session() as session:
|
177 | 193 | req = Request(session)
|
178 | 194 | with self.lock:
|
179 |
| - if self.credentials.valid: |
180 |
| - return # repeat to avoid race (but don't want lock in common case) |
| 195 | + if self._credentials_valid(refresh_buffer): |
| 196 | + return # repeat check to avoid race conditions |
| 197 | + |
181 | 198 | logger.debug("GCS refresh")
|
182 |
| - self.credentials.refresh(req) |
| 199 | + try: |
| 200 | + self.credentials.refresh(req) |
| 201 | + except gauth.exceptions.RefreshError as error: |
| 202 | + # Re-raise as HttpError with a 401 code and the expected message |
| 203 | + raise HttpError( |
| 204 | + {"code": 401, "message": "Invalid Credentials"} |
| 205 | + ) from error |
| 206 | + |
183 | 207 | # https://github.com/fsspec/filesystem_spec/issues/565
|
184 | 208 | self.credentials.apply(self.heads)
|
185 | 209 |
|
|
0 commit comments