|
75 | 75 | BadRequestError, |
76 | 76 | GatedRepoError, |
77 | 77 | HfHubHTTPError, |
| 78 | + LocalTokenNotFoundError, |
78 | 79 | RemoteEntryNotFoundError, |
79 | 80 | RepositoryNotFoundError, |
80 | 81 | RevisionNotFoundError, |
@@ -1694,6 +1695,9 @@ def __init__( |
1694 | 1695 | self.headers = headers |
1695 | 1696 | self._thread_pool: Optional[ThreadPoolExecutor] = None |
1696 | 1697 |
|
| 1698 | + # /whoami-v2 is the only endpoint for which we may want to cache results |
| 1699 | + self._whoami_cache: dict[str, dict] = {} |
| 1700 | + |
1697 | 1701 | def run_as_future(self, fn: Callable[..., R], *args, **kwargs) -> Future[R]: |
1698 | 1702 | """ |
1699 | 1703 | Run a method in the background and return a Future instance. |
@@ -1735,39 +1739,74 @@ def run_as_future(self, fn: Callable[..., R], *args, **kwargs) -> Future[R]: |
1735 | 1739 | return self._thread_pool.submit(fn, *args, **kwargs) |
1736 | 1740 |
|
1737 | 1741 | @validate_hf_hub_args |
1738 | | - def whoami(self, token: Union[bool, str, None] = None) -> dict: |
| 1742 | + def whoami(self, token: Union[bool, str, None] = None, *, cache: bool = False) -> dict: |
1739 | 1743 | """ |
1740 | 1744 | Call HF API to know "whoami". |
1741 | 1745 |
|
| 1746 | + If passing `cache=True`, the result will be cached for subsequent calls for the duration of the Python process. This is useful if you plan to call |
| 1747 | + `whoami` multiple times as this endpoint is heavily rate-limited for security reasons. |
| 1748 | +
|
1742 | 1749 | Args: |
1743 | 1750 | token (`bool` or `str`, *optional*): |
1744 | 1751 | A valid user access token (string). Defaults to the locally saved |
1745 | 1752 | token, which is the recommended method for authentication (see |
1746 | 1753 | https://huggingface.co/docs/huggingface_hub/quick-start#authentication). |
1747 | 1754 | To disable authentication, pass `False`. |
| 1755 | + cache (`bool`, *optional*): |
| 1756 | + Whether to cache the result of the `whoami` call for subsequent calls. |
| 1757 | + If an error occurs during the first call, it won't be cached. |
| 1758 | + Defaults to `False`. |
1748 | 1759 | """ |
1749 | 1760 | # Get the effective token using the helper function get_token |
1750 | | - effective_token = token or self.token or get_token() or True |
| 1761 | + token = self.token if token is None else token |
| 1762 | + if token is False: |
| 1763 | + raise ValueError("Cannot use `token=False` with `whoami` method as it requires authentication.") |
| 1764 | + if token is True or token is None: |
| 1765 | + token = get_token() |
| 1766 | + if token is None: |
| 1767 | + raise LocalTokenNotFoundError( |
| 1768 | + "Token is required to call the /whoami-v2 endpoint, but no token found. You must provide a token or be logged in to " |
| 1769 | + "Hugging Face with `hf auth login` or `huggingface_hub.login`. See https://huggingface.co/settings/tokens." |
| 1770 | + ) |
| 1771 | + |
| 1772 | + if cache and (cached_token := self._whoami_cache.get(token)): |
| 1773 | + return cached_token |
| 1774 | + |
| 1775 | + # Call Hub |
| 1776 | + output = self._inner_whoami(token=token) |
| 1777 | + |
| 1778 | + # Cache result and return |
| 1779 | + if cache: |
| 1780 | + self._whoami_cache[token] = output |
| 1781 | + return output |
| 1782 | + |
| 1783 | + def _inner_whoami(self, token: str) -> dict: |
1751 | 1784 | r = get_session().get( |
1752 | 1785 | f"{self.endpoint}/api/whoami-v2", |
1753 | | - headers=self._build_hf_headers(token=effective_token), |
| 1786 | + headers=self._build_hf_headers(token=token), |
1754 | 1787 | ) |
1755 | 1788 | try: |
1756 | 1789 | hf_raise_for_status(r) |
1757 | 1790 | except HfHubHTTPError as e: |
1758 | 1791 | if e.response.status_code == 401: |
1759 | 1792 | error_message = "Invalid user token." |
1760 | 1793 | # Check which token is the effective one and generate the error message accordingly |
1761 | | - if effective_token == _get_token_from_google_colab(): |
| 1794 | + if token == _get_token_from_google_colab(): |
1762 | 1795 | error_message += " The token from Google Colab vault is invalid. Please update it from the UI." |
1763 | | - elif effective_token == _get_token_from_environment(): |
| 1796 | + elif token == _get_token_from_environment(): |
1764 | 1797 | error_message += ( |
1765 | 1798 | " The token from HF_TOKEN environment variable is invalid. " |
1766 | 1799 | "Note that HF_TOKEN takes precedence over `hf auth login`." |
1767 | 1800 | ) |
1768 | | - elif effective_token == _get_token_from_file(): |
| 1801 | + elif token == _get_token_from_file(): |
1769 | 1802 | error_message += " The token stored is invalid. Please run `hf auth login` to update it." |
1770 | 1803 | raise HfHubHTTPError(error_message, response=e.response) from e |
| 1804 | + if e.response.status_code == 429: |
| 1805 | + error_message = ( |
| 1806 | + "You've hit the rate limit for the /whoami-v2 endpoint, which is intentionally strict for security reasons." |
| 1807 | + " If you're calling it often, consider caching the response with `whoami(..., cache=True)`." |
| 1808 | + ) |
| 1809 | + raise HfHubHTTPError(error_message, response=e.response) from e |
1771 | 1810 | raise |
1772 | 1811 | return r.json() |
1773 | 1812 |
|
|
0 commit comments