| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | """Contains methods to log in to the Hub.""" |
| |
|
| | import os |
| | import subprocess |
| | from getpass import getpass |
| | from pathlib import Path |
| | from typing import Optional |
| |
|
| | from . import constants |
| | from .commands._cli_utils import ANSI |
| | from .utils import ( |
| | capture_output, |
| | get_token, |
| | is_google_colab, |
| | is_notebook, |
| | list_credential_helpers, |
| | logging, |
| | run_subprocess, |
| | set_git_credential, |
| | unset_git_credential, |
| | ) |
| | from .utils._auth import ( |
| | _get_token_by_name, |
| | _get_token_from_environment, |
| | _get_token_from_file, |
| | _get_token_from_google_colab, |
| | _save_stored_tokens, |
| | _save_token, |
| | get_stored_tokens, |
| | ) |
| | from .utils._deprecation import _deprecate_arguments, _deprecate_positional_args |
| |
|
| |
|
| | logger = logging.get_logger(__name__) |
| |
|
| | _HF_LOGO_ASCII = """ |
| | _| _| _| _| _|_|_| _|_|_| _|_|_| _| _| _|_|_| _|_|_|_| _|_| _|_|_| _|_|_|_| |
| | _| _| _| _| _| _| _| _|_| _| _| _| _| _| _| _| |
| | _|_|_|_| _| _| _| _|_| _| _|_| _| _| _| _| _| _|_| _|_|_| _|_|_|_| _| _|_|_| |
| | _| _| _| _| _| _| _| _| _| _| _|_| _| _| _| _| _| _| _| |
| | _| _| _|_| _|_|_| _|_|_| _|_|_| _| _| _|_|_| _| _| _| _|_|_| _|_|_|_| |
| | """ |
| |
|
| |
|
| | @_deprecate_arguments( |
| | version="1.0", |
| | deprecated_args="write_permission", |
| | custom_message="Fine-grained tokens added complexity to the permissions, making it irrelevant to check if a token has 'write' access.", |
| | ) |
| | @_deprecate_positional_args(version="1.0") |
| | def login( |
| | token: Optional[str] = None, |
| | *, |
| | add_to_git_credential: bool = False, |
| | new_session: bool = True, |
| | write_permission: bool = False, |
| | ) -> None: |
| | """Login the machine to access the Hub. |
| | |
| | The `token` is persisted in cache and set as a git credential. Once done, the machine |
| | is logged in and the access token will be available across all `huggingface_hub` |
| | components. If `token` is not provided, it will be prompted to the user either with |
| | a widget (in a notebook) or via the terminal. |
| | |
| | To log in from outside of a script, one can also use `huggingface-cli login` which is |
| | a cli command that wraps [`login`]. |
| | |
| | <Tip> |
| | |
| | [`login`] is a drop-in replacement method for [`notebook_login`] as it wraps and |
| | extends its capabilities. |
| | |
| | </Tip> |
| | |
| | <Tip> |
| | |
| | When the token is not passed, [`login`] will automatically detect if the script runs |
| | in a notebook or not. However, this detection might not be accurate due to the |
| | variety of notebooks that exists nowadays. If that is the case, you can always force |
| | the UI by using [`notebook_login`] or [`interpreter_login`]. |
| | |
| | </Tip> |
| | |
| | Args: |
| | token (`str`, *optional*): |
| | User access token to generate from https://huggingface.co/settings/token. |
| | add_to_git_credential (`bool`, defaults to `False`): |
| | If `True`, token will be set as git credential. If no git credential helper |
| | is configured, a warning will be displayed to the user. If `token` is `None`, |
| | the value of `add_to_git_credential` is ignored and will be prompted again |
| | to the end user. |
| | new_session (`bool`, defaults to `True`): |
| | If `True`, will request a token even if one is already saved on the machine. |
| | write_permission (`bool`): |
| | Ignored and deprecated argument. |
| | Raises: |
| | [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) |
| | If an organization token is passed. Only personal account tokens are valid |
| | to log in. |
| | [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) |
| | If token is invalid. |
| | [`ImportError`](https://docs.python.org/3/library/exceptions.html#ImportError) |
| | If running in a notebook but `ipywidgets` is not installed. |
| | """ |
| | if token is not None: |
| | if not add_to_git_credential: |
| | logger.info( |
| | "The token has not been saved to the git credentials helper. Pass " |
| | "`add_to_git_credential=True` in this function directly or " |
| | "`--add-to-git-credential` if using via `huggingface-cli` if " |
| | "you want to set the git credential as well." |
| | ) |
| | _login(token, add_to_git_credential=add_to_git_credential) |
| | elif is_notebook(): |
| | notebook_login(new_session=new_session) |
| | else: |
| | interpreter_login(new_session=new_session) |
| |
|
| |
|
| | def logout(token_name: Optional[str] = None) -> None: |
| | """Logout the machine from the Hub. |
| | |
| | Token is deleted from the machine and removed from git credential. |
| | |
| | Args: |
| | token_name (`str`, *optional*): |
| | Name of the access token to logout from. If `None`, will logout from all saved access tokens. |
| | Raises: |
| | [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError): |
| | If the access token name is not found. |
| | """ |
| | if get_token() is None and not get_stored_tokens(): |
| | logger.warning("Not logged in!") |
| | return |
| | if not token_name: |
| | |
| | for file_path in (constants.HF_TOKEN_PATH, constants.HF_STORED_TOKENS_PATH): |
| | try: |
| | Path(file_path).unlink() |
| | except FileNotFoundError: |
| | pass |
| | logger.info("Successfully logged out from all access tokens.") |
| | else: |
| | _logout_from_token(token_name) |
| | logger.info(f"Successfully logged out from access token: {token_name}.") |
| |
|
| | unset_git_credential() |
| |
|
| | |
| | if _get_token_from_google_colab() is not None: |
| | raise EnvironmentError( |
| | "You are automatically logged in using a Google Colab secret.\n" |
| | "To log out, you must unset the `HF_TOKEN` secret in your Colab settings." |
| | ) |
| | if _get_token_from_environment() is not None: |
| | raise EnvironmentError( |
| | "Token has been deleted from your machine but you are still logged in.\n" |
| | "To log out, you must clear out both `HF_TOKEN` and `HUGGING_FACE_HUB_TOKEN` environment variables." |
| | ) |
| |
|
| |
|
| | def auth_switch(token_name: str, add_to_git_credential: bool = False) -> None: |
| | """Switch to a different access token. |
| | |
| | Args: |
| | token_name (`str`): |
| | Name of the access token to switch to. |
| | add_to_git_credential (`bool`, defaults to `False`): |
| | If `True`, token will be set as git credential. If no git credential helper |
| | is configured, a warning will be displayed to the user. If `token` is `None`, |
| | the value of `add_to_git_credential` is ignored and will be prompted again |
| | to the end user. |
| | |
| | Raises: |
| | [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError): |
| | If the access token name is not found. |
| | """ |
| | token = _get_token_by_name(token_name) |
| | if not token: |
| | raise ValueError(f"Access token {token_name} not found in {constants.HF_STORED_TOKENS_PATH}") |
| | |
| | _set_active_token(token_name, add_to_git_credential) |
| | logger.info(f"The current active token is: {token_name}") |
| | token_from_environment = _get_token_from_environment() |
| | if token_from_environment is not None and token_from_environment != token: |
| | logger.warning( |
| | "The environment variable `HF_TOKEN` is set and will override the access token you've just switched to." |
| | ) |
| |
|
| |
|
| | def auth_list() -> None: |
| | """List all stored access tokens.""" |
| | tokens = get_stored_tokens() |
| |
|
| | if not tokens: |
| | logger.info("No access tokens found.") |
| | return |
| | |
| | current_token = get_token() |
| | current_token_name = None |
| | for token_name in tokens: |
| | if tokens.get(token_name) == current_token: |
| | current_token_name = token_name |
| | |
| | max_offset = max(len("token"), max(len(token) for token in tokens)) + 2 |
| | print(f" {{:<{max_offset}}}| {{:<15}}".format("name", "token")) |
| | print("-" * (max_offset + 2) + "|" + "-" * 15) |
| |
|
| | |
| | for token_name in tokens: |
| | token = tokens.get(token_name, "<not set>") |
| | masked_token = f"{token[:3]}****{token[-4:]}" if token != "<not set>" else token |
| | is_current = "*" if token == current_token else " " |
| |
|
| | print(f"{is_current} {{:<{max_offset}}}| {{:<15}}".format(token_name, masked_token)) |
| |
|
| | if _get_token_from_environment(): |
| | logger.warning( |
| | "\nNote: Environment variable `HF_TOKEN` is set and is the current active token independently from the stored tokens listed above." |
| | ) |
| | elif current_token_name is None: |
| | logger.warning( |
| | "\nNote: No active token is set and no environment variable `HF_TOKEN` is found. Use `huggingface-cli login` to log in." |
| | ) |
| |
|
| |
|
| | |
| | |
| | |
| |
|
| |
|
| | @_deprecate_arguments( |
| | version="1.0", |
| | deprecated_args="write_permission", |
| | custom_message="Fine-grained tokens added complexity to the permissions, making it irrelevant to check if a token has 'write' access.", |
| | ) |
| | @_deprecate_positional_args(version="1.0") |
| | def interpreter_login(*, new_session: bool = True, write_permission: bool = False) -> None: |
| | """ |
| | Displays a prompt to log in to the HF website and store the token. |
| | |
| | This is equivalent to [`login`] without passing a token when not run in a notebook. |
| | [`interpreter_login`] is useful if you want to force the use of the terminal prompt |
| | instead of a notebook widget. |
| | |
| | For more details, see [`login`]. |
| | |
| | Args: |
| | new_session (`bool`, defaults to `True`): |
| | If `True`, will request a token even if one is already saved on the machine. |
| | write_permission (`bool`): |
| | Ignored and deprecated argument. |
| | """ |
| | if not new_session and get_token() is not None: |
| | logger.info("User is already logged in.") |
| | return |
| |
|
| | from .commands.delete_cache import _ask_for_confirmation_no_tui |
| |
|
| | print(_HF_LOGO_ASCII) |
| | if get_token() is not None: |
| | logger.info( |
| | " A token is already saved on your machine. Run `huggingface-cli" |
| | " whoami` to get more information or `huggingface-cli logout` if you want" |
| | " to log out." |
| | ) |
| | logger.info(" Setting a new token will erase the existing one.") |
| |
|
| | logger.info( |
| | " To log in, `huggingface_hub` requires a token generated from https://huggingface.co/settings/tokens ." |
| | ) |
| | if os.name == "nt": |
| | logger.info("Token can be pasted using 'Right-Click'.") |
| | token = getpass("Enter your token (input will not be visible): ") |
| | add_to_git_credential = _ask_for_confirmation_no_tui("Add token as git credential?") |
| |
|
| | _login(token=token, add_to_git_credential=add_to_git_credential) |
| |
|
| |
|
| | |
| | |
| | |
| |
|
| | NOTEBOOK_LOGIN_PASSWORD_HTML = """<center> <img |
| | src=https://huggingface.co/front/assets/huggingface_logo-noborder.svg |
| | alt='Hugging Face'> <br> Immediately click login after typing your password or |
| | it might be stored in plain text in this notebook file. </center>""" |
| |
|
| |
|
| | NOTEBOOK_LOGIN_TOKEN_HTML_START = """<center> <img |
| | src=https://huggingface.co/front/assets/huggingface_logo-noborder.svg |
| | alt='Hugging Face'> <br> Copy a token from <a |
| | href="https://huggingface.co/settings/tokens" target="_blank">your Hugging Face |
| | tokens page</a> and paste it below. <br> Immediately click login after copying |
| | your token or it might be stored in plain text in this notebook file. </center>""" |
| |
|
| |
|
| | NOTEBOOK_LOGIN_TOKEN_HTML_END = """ |
| | <b>Pro Tip:</b> If you don't already have one, you can create a dedicated |
| | 'notebooks' token with 'write' access, that you can then easily reuse for all |
| | notebooks. </center>""" |
| |
|
| |
|
| | @_deprecate_arguments( |
| | version="1.0", |
| | deprecated_args="write_permission", |
| | custom_message="Fine-grained tokens added complexity to the permissions, making it irrelevant to check if a token has 'write' access.", |
| | ) |
| | @_deprecate_positional_args(version="1.0") |
| | def notebook_login(*, new_session: bool = True, write_permission: bool = False) -> None: |
| | """ |
| | Displays a widget to log in to the HF website and store the token. |
| | |
| | This is equivalent to [`login`] without passing a token when run in a notebook. |
| | [`notebook_login`] is useful if you want to force the use of the notebook widget |
| | instead of a prompt in the terminal. |
| | |
| | For more details, see [`login`]. |
| | |
| | Args: |
| | new_session (`bool`, defaults to `True`): |
| | If `True`, will request a token even if one is already saved on the machine. |
| | write_permission (`bool`): |
| | Ignored and deprecated argument. |
| | """ |
| | try: |
| | import ipywidgets.widgets as widgets |
| | from IPython.display import display |
| | except ImportError: |
| | raise ImportError( |
| | "The `notebook_login` function can only be used in a notebook (Jupyter or" |
| | " Colab) and you need the `ipywidgets` module: `pip install ipywidgets`." |
| | ) |
| | if not new_session and get_token() is not None: |
| | logger.info("User is already logged in.") |
| | return |
| |
|
| | box_layout = widgets.Layout(display="flex", flex_flow="column", align_items="center", width="50%") |
| |
|
| | token_widget = widgets.Password(description="Token:") |
| | git_checkbox_widget = widgets.Checkbox(value=True, description="Add token as git credential?") |
| | token_finish_button = widgets.Button(description="Login") |
| |
|
| | login_token_widget = widgets.VBox( |
| | [ |
| | widgets.HTML(NOTEBOOK_LOGIN_TOKEN_HTML_START), |
| | token_widget, |
| | git_checkbox_widget, |
| | token_finish_button, |
| | widgets.HTML(NOTEBOOK_LOGIN_TOKEN_HTML_END), |
| | ], |
| | layout=box_layout, |
| | ) |
| | display(login_token_widget) |
| |
|
| | |
| | def login_token_event(t): |
| | """Event handler for the login button.""" |
| | token = token_widget.value |
| | add_to_git_credential = git_checkbox_widget.value |
| | |
| | token_widget.value = "" |
| | |
| | login_token_widget.children = [widgets.Label("Connecting...")] |
| | try: |
| | with capture_output() as captured: |
| | _login(token, add_to_git_credential=add_to_git_credential) |
| | message = captured.getvalue() |
| | except Exception as error: |
| | message = str(error) |
| | |
| | login_token_widget.children = [widgets.Label(line) for line in message.split("\n") if line.strip()] |
| |
|
| | token_finish_button.on_click(login_token_event) |
| |
|
| |
|
| | |
| | |
| | |
| |
|
| |
|
| | def _login( |
| | token: str, |
| | add_to_git_credential: bool, |
| | ) -> None: |
| | from .hf_api import whoami |
| |
|
| | if token.startswith("api_org"): |
| | raise ValueError("You must use your personal account token, not an organization token.") |
| |
|
| | token_info = whoami(token) |
| | permission = token_info["auth"]["accessToken"]["role"] |
| | logger.info(f"Token is valid (permission: {permission}).") |
| |
|
| | token_name = token_info["auth"]["accessToken"]["displayName"] |
| | |
| | _save_token(token=token, token_name=token_name) |
| | |
| | _set_active_token(token_name=token_name, add_to_git_credential=add_to_git_credential) |
| | logger.info("Login successful.") |
| | if _get_token_from_environment(): |
| | logger.warning( |
| | "Note: Environment variable`HF_TOKEN` is set and is the current active token independently from the token you've just configured." |
| | ) |
| | else: |
| | logger.info(f"The current active token is: `{token_name}`") |
| |
|
| |
|
| | def _logout_from_token(token_name: str) -> None: |
| | """Logout from a specific access token. |
| | |
| | Args: |
| | token_name (`str`): |
| | The name of the access token to logout from. |
| | Raises: |
| | [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError): |
| | If the access token name is not found. |
| | """ |
| | stored_tokens = get_stored_tokens() |
| | |
| | if not stored_tokens or token_name not in stored_tokens: |
| | return |
| |
|
| | token = stored_tokens.pop(token_name) |
| | _save_stored_tokens(stored_tokens) |
| |
|
| | if token == _get_token_from_file(): |
| | logger.warning(f"Active token '{token_name}' has been deleted.") |
| | Path(constants.HF_TOKEN_PATH).unlink(missing_ok=True) |
| |
|
| |
|
| | def _set_active_token( |
| | token_name: str, |
| | add_to_git_credential: bool, |
| | ) -> None: |
| | """Set the active access token. |
| | |
| | Args: |
| | token_name (`str`): |
| | The name of the token to set as active. |
| | """ |
| | token = _get_token_by_name(token_name) |
| | if not token: |
| | raise ValueError(f"Token {token_name} not found in {constants.HF_STORED_TOKENS_PATH}") |
| | if add_to_git_credential: |
| | if _is_git_credential_helper_configured(): |
| | set_git_credential(token) |
| | logger.info( |
| | "Your token has been saved in your configured git credential helpers" |
| | + f" ({','.join(list_credential_helpers())})." |
| | ) |
| | else: |
| | logger.warning("Token has not been saved to git credential helper.") |
| | |
| | path = Path(constants.HF_TOKEN_PATH) |
| | path.parent.mkdir(parents=True, exist_ok=True) |
| | path.write_text(token) |
| | logger.info(f"Your token has been saved to {constants.HF_TOKEN_PATH}") |
| |
|
| |
|
| | def _is_git_credential_helper_configured() -> bool: |
| | """Check if a git credential helper is configured. |
| | |
| | Warns user if not the case (except for Google Colab where "store" is set by default |
| | by `huggingface_hub`). |
| | """ |
| | helpers = list_credential_helpers() |
| | if len(helpers) > 0: |
| | return True |
| |
|
| | |
| | |
| | if is_google_colab(): |
| | _set_store_as_git_credential_helper_globally() |
| | return True |
| |
|
| | |
| | print( |
| | ANSI.red( |
| | "Cannot authenticate through git-credential as no helper is defined on your" |
| | " machine.\nYou might have to re-authenticate when pushing to the Hugging" |
| | " Face Hub.\nRun the following command in your terminal in case you want to" |
| | " set the 'store' credential helper as default.\n\ngit config --global" |
| | " credential.helper store\n\nRead" |
| | " https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage for more" |
| | " details." |
| | ) |
| | ) |
| | return False |
| |
|
| |
|
| | def _set_store_as_git_credential_helper_globally() -> None: |
| | """Set globally the credential.helper to `store`. |
| | |
| | To be used only in Google Colab as we assume the user doesn't care about the git |
| | credential config. It is the only particular case where we don't want to display the |
| | warning message in [`notebook_login()`]. |
| | |
| | Related: |
| | - https://github.com/huggingface/huggingface_hub/issues/1043 |
| | - https://github.com/huggingface/huggingface_hub/issues/1051 |
| | - https://git-scm.com/docs/git-credential-store |
| | """ |
| | try: |
| | run_subprocess("git config --global credential.helper store") |
| | except subprocess.CalledProcessError as exc: |
| | raise EnvironmentError(exc.stderr) |
| |
|