File size: 13,697 Bytes
c8e7ce2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
import re
from typing import Optional
from requests import HTTPError, Response
from ._fixes import JSONDecodeError
REPO_API_REGEX = re.compile(
r"""
# staging or production endpoint
^https://(hub-ci.)?huggingface.co
(
# on /api/repo_type/repo_id
/api/(models|datasets|spaces)/(.+)
|
# or /repo_id/resolve/revision/...
/(.+)/resolve/(.+)
)
""",
flags=re.VERBOSE,
)
class FileMetadataError(OSError):
"""Error triggered when the metadata of a file on the Hub cannot be retrieved (missing ETag or commit_hash).
Inherits from `OSError` for backward compatibility.
"""
class HfHubHTTPError(HTTPError):
"""
HTTPError to inherit from for any custom HTTP Error raised in HF Hub.
Any HTTPError is converted at least into a `HfHubHTTPError`. If some information is
sent back by the server, it will be added to the error message.
Added details:
- Request id from "X-Request-Id" header if exists.
- Server error message from the header "X-Error-Message".
- Server error message if we can found one in the response body.
Example:
```py
import requests
from huggingface_hub.utils import get_session, hf_raise_for_status, HfHubHTTPError
response = get_session().post(...)
try:
hf_raise_for_status(response)
except HfHubHTTPError as e:
print(str(e)) # formatted message
e.request_id, e.server_message # details returned by server
# Complete the error message with additional information once it's raised
e.append_to_message("\n`create_commit` expects the repository to exist.")
raise
```
"""
request_id: Optional[str] = None
server_message: Optional[str] = None
def __init__(self, message: str, response: Optional[Response] = None):
# Parse server information if any.
if response is not None:
self.request_id = response.headers.get("X-Request-Id")
try:
server_data = response.json()
except JSONDecodeError:
server_data = {}
# Retrieve server error message from multiple sources
server_message_from_headers = response.headers.get("X-Error-Message")
server_message_from_body = server_data.get("error")
server_multiple_messages_from_body = "\n".join(
error["message"] for error in server_data.get("errors", []) if "message" in error
)
# Concatenate error messages
_server_message = ""
if server_message_from_headers is not None: # from headers
_server_message += server_message_from_headers + "\n"
if server_message_from_body is not None: # from body "error"
if isinstance(server_message_from_body, list):
server_message_from_body = "\n".join(server_message_from_body)
if server_message_from_body not in _server_message:
_server_message += server_message_from_body + "\n"
if server_multiple_messages_from_body is not None: # from body "errors"
if server_multiple_messages_from_body not in _server_message:
_server_message += server_multiple_messages_from_body + "\n"
_server_message = _server_message.strip()
# Set message to `HfHubHTTPError` (if any)
if _server_message != "":
self.server_message = _server_message
super().__init__(
_format_error_message(
message,
request_id=self.request_id,
server_message=self.server_message,
),
response=response, # type: ignore
request=response.request if response is not None else None, # type: ignore
)
def append_to_message(self, additional_message: str) -> None:
"""Append additional information to the `HfHubHTTPError` initial message."""
self.args = (self.args[0] + additional_message,) + self.args[1:]
class RepositoryNotFoundError(HfHubHTTPError):
"""
Raised when trying to access a hf.co URL with an invalid repository name, or
with a private repo name the user does not have access to.
Example:
```py
>>> from huggingface_hub import model_info
>>> model_info("<non_existent_repository>")
(...)
huggingface_hub.utils._errors.RepositoryNotFoundError: 401 Client Error. (Request ID: PvMw_VjBMjVdMz53WKIzP)
Repository Not Found for url: https://huggingface.co/api/models/%3Cnon_existent_repository%3E.
Please make sure you specified the correct `repo_id` and `repo_type`.
If the repo is private, make sure you are authenticated.
Invalid username or password.
```
"""
class GatedRepoError(RepositoryNotFoundError):
"""
Raised when trying to access a gated repository for which the user is not on the
authorized list.
Note: derives from `RepositoryNotFoundError` to ensure backward compatibility.
Example:
```py
>>> from huggingface_hub import model_info
>>> model_info("<gated_repository>")
(...)
huggingface_hub.utils._errors.GatedRepoError: 403 Client Error. (Request ID: ViT1Bf7O_026LGSQuVqfa)
Cannot access gated repo for url https://huggingface.co/api/models/ardent-figment/gated-model.
Access to model ardent-figment/gated-model is restricted and you are not in the authorized list.
Visit https://huggingface.co/ardent-figment/gated-model to ask for access.
```
"""
class RevisionNotFoundError(HfHubHTTPError):
"""
Raised when trying to access a hf.co URL with a valid repository but an invalid
revision.
Example:
```py
>>> from huggingface_hub import hf_hub_download
>>> hf_hub_download('bert-base-cased', 'config.json', revision='<non-existent-revision>')
(...)
huggingface_hub.utils._errors.RevisionNotFoundError: 404 Client Error. (Request ID: Mwhe_c3Kt650GcdKEFomX)
Revision Not Found for url: https://huggingface.co/bert-base-cased/resolve/%3Cnon-existent-revision%3E/config.json.
```
"""
class EntryNotFoundError(HfHubHTTPError):
"""
Raised when trying to access a hf.co URL with a valid repository and revision
but an invalid filename.
Example:
```py
>>> from huggingface_hub import hf_hub_download
>>> hf_hub_download('bert-base-cased', '<non-existent-file>')
(...)
huggingface_hub.utils._errors.EntryNotFoundError: 404 Client Error. (Request ID: 53pNl6M0MxsnG5Sw8JA6x)
Entry Not Found for url: https://huggingface.co/bert-base-cased/resolve/main/%3Cnon-existent-file%3E.
```
"""
class LocalEntryNotFoundError(EntryNotFoundError, FileNotFoundError, ValueError):
"""
Raised when trying to access a file or snapshot that is not on the disk when network is
disabled or unavailable (connection issue). The entry may exist on the Hub.
Note: `ValueError` type is to ensure backward compatibility.
Note: `LocalEntryNotFoundError` derives from `HTTPError` because of `EntryNotFoundError`
even when it is not a network issue.
Example:
```py
>>> from huggingface_hub import hf_hub_download
>>> hf_hub_download('bert-base-cased', '<non-cached-file>', local_files_only=True)
(...)
huggingface_hub.utils._errors.LocalEntryNotFoundError: Cannot find the requested files in the disk cache and outgoing traffic has been disabled. To enable hf.co look-ups and downloads online, set 'local_files_only' to False.
```
"""
def __init__(self, message: str):
super().__init__(message, response=None)
class BadRequestError(HfHubHTTPError, ValueError):
"""
Raised by `hf_raise_for_status` when the server returns a HTTP 400 error.
Example:
```py
>>> resp = requests.post("hf.co/api/check", ...)
>>> hf_raise_for_status(resp, endpoint_name="check")
huggingface_hub.utils._errors.BadRequestError: Bad request for check endpoint: {details} (Request ID: XXX)
```
"""
def hf_raise_for_status(response: Response, endpoint_name: Optional[str] = None) -> None:
"""
Internal version of `response.raise_for_status()` that will refine a
potential HTTPError. Raised exception will be an instance of `HfHubHTTPError`.
This helper is meant to be the unique method to raise_for_status when making a call
to the Hugging Face Hub.
Example:
```py
import requests
from huggingface_hub.utils import get_session, hf_raise_for_status, HfHubHTTPError
response = get_session().post(...)
try:
hf_raise_for_status(response)
except HfHubHTTPError as e:
print(str(e)) # formatted message
e.request_id, e.server_message # details returned by server
# Complete the error message with additional information once it's raised
e.append_to_message("\n`create_commit` expects the repository to exist.")
raise
```
Args:
response (`Response`):
Response from the server.
endpoint_name (`str`, *optional*):
Name of the endpoint that has been called. If provided, the error message
will be more complete.
<Tip warning={true}>
Raises when the request has failed:
- [`~utils.RepositoryNotFoundError`]
If the repository to download from cannot be found. This may be because it
doesn't exist, because `repo_type` is not set correctly, or because the repo
is `private` and you do not have access.
- [`~utils.GatedRepoError`]
If the repository exists but is gated and the user is not on the authorized
list.
- [`~utils.RevisionNotFoundError`]
If the repository exists but the revision couldn't be find.
- [`~utils.EntryNotFoundError`]
If the repository exists but the entry (e.g. the requested file) couldn't be
find.
- [`~utils.BadRequestError`]
If request failed with a HTTP 400 BadRequest error.
- [`~utils.HfHubHTTPError`]
If request failed for a reason not listed above.
</Tip>
"""
try:
response.raise_for_status()
except HTTPError as e:
error_code = response.headers.get("X-Error-Code")
if error_code == "RevisionNotFound":
message = f"{response.status_code} Client Error." + "\n\n" + f"Revision Not Found for url: {response.url}."
raise RevisionNotFoundError(message, response) from e
elif error_code == "EntryNotFound":
message = f"{response.status_code} Client Error." + "\n\n" + f"Entry Not Found for url: {response.url}."
raise EntryNotFoundError(message, response) from e
elif error_code == "GatedRepo":
message = (
f"{response.status_code} Client Error." + "\n\n" + f"Cannot access gated repo for url {response.url}."
)
raise GatedRepoError(message, response) from e
elif error_code == "RepoNotFound" or (
response.status_code == 401
and response.request is not None
and response.request.url is not None
and REPO_API_REGEX.search(response.request.url) is not None
):
# 401 is misleading as it is returned for:
# - private and gated repos if user is not authenticated
# - missing repos
# => for now, we process them as `RepoNotFound` anyway.
# See https://gist.github.com/Wauplin/46c27ad266b15998ce56a6603796f0b9
message = (
f"{response.status_code} Client Error."
+ "\n\n"
+ f"Repository Not Found for url: {response.url}."
+ "\nPlease make sure you specified the correct `repo_id` and"
" `repo_type`.\nIf you are trying to access a private or gated repo,"
" make sure you are authenticated."
)
raise RepositoryNotFoundError(message, response) from e
elif response.status_code == 400:
message = (
f"\n\nBad request for {endpoint_name} endpoint:" if endpoint_name is not None else "\n\nBad request:"
)
raise BadRequestError(message, response=response) from e
# Convert `HTTPError` into a `HfHubHTTPError` to display request information
# as well (request id and/or server error message)
raise HfHubHTTPError(str(e), response=response) from e
def _format_error_message(message: str, request_id: Optional[str], server_message: Optional[str]) -> str:
"""
Format the `HfHubHTTPError` error message based on initial message and information
returned by the server.
Used when initializing `HfHubHTTPError`.
"""
# Add message from response body
if server_message is not None and len(server_message) > 0 and server_message.lower() not in message.lower():
if "\n\n" in message:
message += "\n" + server_message
else:
message += "\n\n" + server_message
# Add Request ID
if request_id is not None and str(request_id).lower() not in message.lower():
request_id_message = f" (Request ID: {request_id})"
if "\n" in message:
newline_index = message.index("\n")
message = message[:newline_index] + request_id_message + message[newline_index:]
else:
message += request_id_message
return message
|