Repository: huggingface/huggingface_hub
Pull request #810 (2022-03-31 07:04:12)

MNT change dev version to 0.5, 0.4 is already released
We're releasing 0.5, not 0.4.



LysandreJik: Re-running the tests now that the inference-api rate limit was fixed.
HuggingFaceDocBuilderDev: The docs for this PR live [here](https://moon-ci-docs.huggingface.co/docs/huggingface_hub/pr_810). All of your documentation changes will be reflected on that endpoint.
adrinjalali: ```python-traceback
self = <tests.test_hf_api.HfApiPublicTest testMethod=test_model_info_with_security>

    @with_production_testing
    def test_model_info_with_security(self):
        _api = HfApi()
        model = _api.model_info(
            repo_id=DUMMY_MODEL_ID,
            revision=DUMMY_MODEL_ID_REVISION_ONE_SPECIFIC_COMMIT,
            securityStatus=True,
        )
        self.assertEqual(
>           getattr(model, "securityStatus"),
            {"containsInfected": False, "infectionTypes": []},
        )
E       AttributeError: 'ModelInfo' object has no attribute 'securityStatus'
```

we've seen this before, didn't we fix it?
adrinjalali: O changed the version to `0.5` instead of `0.5.0` since minor bugfix number is irrelevant for the purpose of people knowing when something was added or changed (I think)

Patch:
diff --git a/src/huggingface_hub/__init__.py b/src/huggingface_hub/__init__.py
index 7ee56f68..1a92ba05 100644
--- a/src/huggingface_hub/__init__.py
+++ b/src/huggingface_hub/__init__.py
@@ -16,7 +16,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-__version__ = "0.4.0.dev0"
+__version__ = "0.5.0.dev0"
 
 from .commands.user import notebook_login
 from .constants import (
diff --git a/src/huggingface_hub/commands/user.py b/src/huggingface_hub/commands/user.py
index 2655244f..db4b3216 100644
--- a/src/huggingface_hub/commands/user.py
+++ b/src/huggingface_hub/commands/user.py
@@ -382,8 +382,8 @@ def _login(hf_api, username=None, password=None, token=None):
             print(e)
             print(ANSI.red(e.response.text))
             exit(1)
-    elif not hf_api._is_valid_token(token):
-        raise ValueError("Invalid token passed.")
+    else:
+        token, name = hf_api._validate_or_retrieve_token(token)
 
     hf_api.set_access_token(token)
     HfFolder.save_token(token)
diff --git a/src/huggingface_hub/hf_api.py b/src/huggingface_hub/hf_api.py
index 2c08cf61..a8a48139 100644
--- a/src/huggingface_hub/hf_api.py
+++ b/src/huggingface_hub/hf_api.py
@@ -523,7 +523,6 @@ class HfApi:
                 "You need to pass a valid `token` or login by using `huggingface-cli "
                 "login`"
             )
-
         path = f"{self.endpoint}/api/whoami-v2"
         r = requests.get(path, headers={"authorization": f"Bearer {token}"})
         try:
@@ -553,19 +552,26 @@ class HfApi:
         except HTTPError:
             return False
 
-    def _validate_or_retrieve_token(self, token: Optional[Union[str, bool]] = None):
+    def _validate_or_retrieve_token(
+        self,
+        token: Optional[str] = None,
+        name: Optional[str] = None,
+        function_name: Optional[str] = None,
+    ):
         """
-        Either retrieves stored token or validates passed token.
-
+        Retrieves and validates stored token or validates passed token.
         Args:
-            token (`str`, *optional*):
-                The token to check for validity
-
+            token (``str``, `optional`):
+                Hugging Face token. Will default to the locally saved token if not provided.
+            name (``str``, `optional`):
+                Name of the repository. This is deprecated in favor of repo_id and will be removed in v0.7.
+            function_name (``str``, `optional`):
+                If _validate_or_retrieve_token is called from a function, name of that function to be passed inside deprecation warning.
         Returns:
-            `str`: The valid token
-
+            Validated token and the name of the repository.
         Raises:
-            `ValueError`: if the token is invalid.
+            :class:`EnvironmentError`: If the token is not passed and there's no token saved locally.
+            :class:`ValueError`: If organization token or invalid token is passed.
         """
         if token is None or token is True:
             token = HfFolder.get_token()
@@ -574,9 +580,22 @@ class HfApi:
                     "You need to provide a `token` or be logged in to Hugging "
                     "Face with `huggingface-cli login`."
                 )
-        elif not self._is_valid_token(token):
-            raise ValueError("Invalid token passed!")
-        return token
+        if name is not None:
+            if self._is_valid_token(name):
+                # TODO(0.6) REMOVE
+                warnings.warn(
+                    f"`{function_name}` now takes `token` as an optional positional argument. "
+                    "Be sure to adapt your code!",
+                    FutureWarning,
+                )
+                token, name = name, token
+        if isinstance(token, str):
+            if token.startswith("api_org"):
+                raise ValueError("You must use your personal account token.")
+            if not self._is_valid_token(token):
+                raise ValueError("Invalid token passed!")
+
+        return token, name
 
     def logout(self, token: Optional[str] = None) -> None:
         """
@@ -758,7 +777,7 @@ class HfApi:
         """
         path = f"{self.endpoint}/api/models"
         if use_auth_token:
-            token = self._validate_or_retrieve_token(use_auth_token)
+            token, name = self._validate_or_retrieve_token(use_auth_token)
         headers = {"authorization": f"Bearer {token}"} if use_auth_token else None
         params = {}
         if filter is not None:
@@ -955,7 +974,7 @@ class HfApi:
         """
         path = f"{self.endpoint}/api/datasets"
         if use_auth_token:
-            token = self._validate_or_retrieve_token(use_auth_token)
+            token, name = self._validate_or_retrieve_token(use_auth_token)
         headers = {"authorization": f"Bearer {token}"} if use_auth_token else None
         params = {}
         if filter is not None:
@@ -1212,7 +1231,7 @@ class HfApi:
 
                 <Tip>
 
-                Version added: 0.4.0
+                Version added: 0.5
 
                 </Tip>
 
@@ -1240,18 +1259,10 @@ class HfApi:
         name, organization = _validate_repo_id_deprecation(repo_id, name, organization)
 
         path = f"{self.endpoint}/api/repos/create"
-        if token is None:
-            token = self._validate_or_retrieve_token()
-        elif not self._is_valid_token(token):
-            if self._is_valid_token(name):
-                warnings.warn(
-                    "`create_repo` now takes `token` as an optional positional argument. "
-                    "Be sure to adapt your code!",
-                    FutureWarning,
-                )
-                token, name = name, token
-            else:
-                raise ValueError("Invalid token passed!")
+
+        token, name = self._validate_or_retrieve_token(
+            token, name, function_name="create_repo"
+        )
 
         checked_name = repo_type_and_id_from_hf_id(name)
 
@@ -1352,7 +1363,7 @@ class HfApi:
 
                 <Tip>
 
-               Version added: 0.4.0
+                Version added: 0.5
 
                 </Tip>
 
@@ -1369,18 +1380,10 @@ class HfApi:
         name, organization = _validate_repo_id_deprecation(repo_id, name, organization)
 
         path = f"{self.endpoint}/api/repos/delete"
-        if token is None:
-            token = self._validate_or_retrieve_token()
-        elif not self._is_valid_token(token):
-            if self._is_valid_token(name):
-                warnings.warn(
-                    "`delete_repo` now takes `token` as an optional positional argument. "
-                    "Be sure to adapt your code!",
-                    FutureWarning,
-                )
-                token, name = name, token
-            else:
-                raise ValueError("Invalid token passed!")
+
+        token, name = self._validate_or_retrieve_token(
+            token, name, function_name="delete_repo"
+        )
 
         checked_name = repo_type_and_id_from_hf_id(name)
 
@@ -1455,9 +1458,9 @@ class HfApi:
 
                 <Tip>
 
-                 Version added: 0.4.0
+                Version added: 0.5
 
-                 </Tip>
+                </Tip>
 
             private (`bool`, *optional*, defaults to `False`):
                 Whether the model repo should be private.
@@ -1480,18 +1483,9 @@ class HfApi:
 
         name, organization = _validate_repo_id_deprecation(repo_id, name, organization)
 
-        if token is None:
-            token = self._validate_or_retrieve_token()
-        elif not self._is_valid_token(token):
-            if self._is_valid_token(name):
-                warnings.warn(
-                    "`update_repo_visibility` now takes `token` as an optional positional argument. "
-                    "Be sure to adapt your code!",
-                    FutureWarning,
-                )
-                token, name, private = name, private, token
-            else:
-                raise ValueError("Invalid token passed!")
+        token, name = self._validate_or_retrieve_token(
+            token, name, function_name="update_repo_visibility"
+        )
 
         if organization is None:
             namespace = self.whoami(token)["name"]
@@ -1548,7 +1542,8 @@ class HfApi:
 
         - [1] https://huggingface.co/settings/tokens
         """
-        token = self._validate_or_retrieve_token(token)
+
+        token, name = self._validate_or_retrieve_token(token)
 
         if len(from_id.split("/")) != 2:
             raise ValueError(
@@ -1664,9 +1659,11 @@ class HfApi:
         if repo_type not in REPO_TYPES:
             raise ValueError(f"Invalid repo type, must be one of {REPO_TYPES}")
 
-        if token is None:
-            token = self._validate_or_retrieve_token()
-        elif not self._is_valid_token(token):
+        try:
+            token, name = self._validate_or_retrieve_token(
+                token, function_name="upload_file"
+            )
+        except ValueError:  # if token is invalid or organization token
             if self._is_valid_token(path_or_fileobj):
                 warnings.warn(
                     "`upload_file` now takes `token` as an optional positional argument. "
@@ -1769,8 +1766,7 @@ class HfApi:
         if repo_type not in REPO_TYPES:
             raise ValueError(f"Invalid repo type, must be one of {REPO_TYPES}")
 
-        if token is None:
-            token = self._validate_or_retrieve_token()
+        token, name = self._validate_or_retrieve_token(token)
 
         if repo_type in REPO_TYPES_URL_PREFIXES:
             repo_id = REPO_TYPES_URL_PREFIXES[repo_type] + repo_id
diff --git a/src/huggingface_hub/hub_mixin.py b/src/huggingface_hub/hub_mixin.py
index 75f9d27d..ff90c4df 100644
--- a/src/huggingface_hub/hub_mixin.py
+++ b/src/huggingface_hub/hub_mixin.py
@@ -276,7 +276,7 @@ class ModelHubMixin:
             token = HfFolder.get_token()
             if token is None:
                 raise ValueError(
-                    "You must login to the Hugging Face hub on this computer by typing `transformers-cli login` and "
+                    "You must login to the Hugging Face hub on this computer by typing `huggingface-cli login` and "
                     "entering your credentials to use `use_auth_token=True`. Alternatively, you can pass your own "
                     "token as the `use_auth_token` argument."
                 )


Test patch:
diff --git a/tests/test_hf_api.py b/tests/test_hf_api.py
index b7df67a0..d90e3da6 100644
--- a/tests/test_hf_api.py
+++ b/tests/test_hf_api.py
@@ -151,6 +151,12 @@ class HfApiLoginTest(HfApiCommonTest):
             read_from_credential_store(USERNAME_PLACEHOLDER), (None, None)
         )
 
+    def test_login_cli_org_fail(self):
+        with pytest.raises(
+            ValueError, match="You must use your personal account token."
+        ):
+            _login(self._api, token="api_org_dummy_token")
+
     def test_login_deprecation_error(self):
         with pytest.warns(
             FutureWarning,
@@ -561,6 +567,23 @@ class HfApiUploadFileTest(HfApiCommonTestWithLogin):
         finally:
             self._api.delete_repo(repo_id=REPO_NAME, token=self._token)
 
+    @retry_endpoint
+    def test_create_repo_org_token_fail(self):
+        REPO_NAME = repo_name("org")
+        with pytest.raises(
+            ValueError, match="You must use your personal account token."
+        ):
+            self._api.create_repo(repo_id=REPO_NAME, token="api_org_dummy_token")
+
+    @retry_endpoint
+    def test_create_repo_org_token_none_fail(self):
+        REPO_NAME = repo_name("org")
+        HfFolder.save_token("api_org_dummy_token")
+        with pytest.raises(
+            ValueError, match="You must use your personal account token."
+        ):
+            self._api.create_repo(repo_id=REPO_NAME)
+
     @retry_endpoint
     def test_upload_file_conflict(self):
         REPO_NAME = repo_name("conflict")