Repository: huggingface/huggingface_hub
Pull request #1157 (2022-11-04 10:39:50)

Use default `model_name` in `metadata_update`
As reported by a user, the `metadata_update` does not work anymore (likely introduced in #940) if no `"name"` field in the `"model-index"` is provided. This breaks for example `evaluate.push_to_hub` where evaluation results are pushed to the hub. 

With this PR the following behaviour is supported:
- if `"model-index"` of new metadata has a `"name"` it is used (1)
- if there is no `"name"`
    - if the existing repo card has one use this one (2)
    - else use the repo name as default (3)

Since the `"name"` is I think a little known feature of the PwC integration and providing a model name when pushing the results to a model repo seems a bit redundant I feel like having good defaults and not bother the user with it is a good idea. If you think this is a bad idea, we can throw an error in case (3).

cc @julien-c 

HuggingFaceDocBuilderDev: The docs for this PR live [here](https://moon-ci-docs.huggingface.co/docs/huggingface_hub/pr_1157). All of your documentation changes will be reflected on that endpoint.
Wauplin: > I think a little known feature (...) I feel like having good defaults and not bother the user with it is a good idea.

To be honest, I don't know enough about metadata to really have an opinion on the "name" under "model-index". But I agree with you that in general we don't want to bother users about very specific features. I ping @nateraw would worked on that and might be more opinionated.  

---

Otherwise, I noticed that for (3) you are talking about the repo name (e.g. "bart-base") and not the repo id (e.g. "facebook/bart-base"). I guess that's intended but wanted to highlight it for others in the discussion.

And also if we move forward on this PR, @lvwerra  could you please add at least one test that triggers case (2) or (3) (e.g. a case that is currently broken but that your PR solves). Thanks in advance !
lvwerra: Yes, will add tests if people are happy with the suggested change, thanks @Wauplin!

Patch:
diff --git a/docs/source/how-to-discussions-and-pull-requests.mdx b/docs/source/how-to-discussions-and-pull-requests.mdx
index d6ecab93..353deeda 100644
--- a/docs/source/how-to-discussions-and-pull-requests.mdx
+++ b/docs/source/how-to-discussions-and-pull-requests.mdx
@@ -76,6 +76,7 @@ set the `create_pr` parameter to `True`. This parameter is also available on oth
     * [`upload_file`]
     * [`upload_folder`]
     * [`delete_file`]
+    * [`delete_folder`]
     * [`metadata_update`]
 
 ```python
diff --git a/docs/source/how-to-upstream.mdx b/docs/source/how-to-upstream.mdx
index b73614d4..061c79bf 100644
--- a/docs/source/how-to-upstream.mdx
+++ b/docs/source/how-to-upstream.mdx
@@ -78,16 +78,16 @@ If both `allow_patterns` and `ignore_patterns` are provided, both constraints ap
 
 If you want to work at a commit-level, use the [`create_commit`] function directly. There are two types of operations supported by [`create_commit`]:
 
-- `CommitOperationAdd` uploads a file to the Hub. If the file already exists, the file contents are overwritten. This operation accepts two arguments:
+- [`CommitOperationAdd`] uploads a file to the Hub. If the file already exists, the file contents are overwritten. This operation accepts two arguments:
 
   - `path_in_repo`: the repository path to upload a file to.
   - `path_or_fileobj`: either a path to a file on your filesystem or a file-like object. This is the content of the file to upload to the Hub.
 
-- `CommitOperationDelete` removes a file from a repository. This operation accepts `path_in_repo` as an argument.
+- [`CommitOperationDelete`] removes a file or a folder from a repository. This operation accepts `path_in_repo` as an argument.
 
 For example, if you want to upload two files and delete a file in a Hub repository:
 
-1. Use the appropriate `CommitOperation` to add and delete a file:
+1. Use the appropriate `CommitOperation` to add or delete a file and to delete a folder:
 
 ```py
 >>> from huggingface_hub import HfApi, CommitOperationAdd, CommitOperationDelete
@@ -96,6 +96,7 @@ For example, if you want to upload two files and delete a file in a Hub reposito
 ...     CommitOperationAdd(path_in_repo="LICENSE.md", path_or_fileobj="~/repo/LICENSE.md"),
 ...     CommitOperationAdd(path_in_repo="weights.h5", path_or_fileobj="~/repo/weights-final.h5"),
 ...     CommitOperationDelete(path_in_repo="old-weights.h5"),
+...     CommitOperationDelete(path_in_repo="logs/"),
 ... ]
 ```
 
@@ -112,6 +113,7 @@ For example, if you want to upload two files and delete a file in a Hub reposito
 In addition to [`upload_file`] and [`upload_folder`], the following functions also use [`create_commit`] under the hood:
 
 - [`delete_file`] deletes a single file from a repository on the Hub.
+- [`delete_folder`] deletes an entire folder from a repository on the Hub.
 - [`metadata_update`] updates a repository's metadata.
 
 For more detailed information, take a look at the [`HfApi`] reference.
diff --git a/setup.py b/setup.py
index 2d5fa4e2..4ccd9281 100644
--- a/setup.py
+++ b/setup.py
@@ -66,7 +66,7 @@ extras["quality"] = [
     "flake8>=3.8.3",
     "flake8-bugbear",
     "isort>=5.5.4",
-    "mypy",
+    "mypy==0.982",
 ]
 
 extras["all"] = extras["testing"] + extras["quality"] + extras["typing"]
diff --git a/src/huggingface_hub/README.md b/src/huggingface_hub/README.md
index 076667e5..df6e9aae 100644
--- a/src/huggingface_hub/README.md
+++ b/src/huggingface_hub/README.md
@@ -89,6 +89,7 @@ single file to a repo or listing models from the Hub, you'll find helpers in
 * `create_commit()`
 * `upload_file()`
 * `delete_file()`
+* `delete_folder()`
 
 Those API utilities are also exposed through the `huggingface-cli` CLI:
 
diff --git a/src/huggingface_hub/__init__.py b/src/huggingface_hub/__init__.py
index 4f89f6e9..08f1fc26 100644
--- a/src/huggingface_hub/__init__.py
+++ b/src/huggingface_hub/__init__.py
@@ -112,6 +112,7 @@ _SUBMOD_ATTRS = {
         "create_tag",
         "dataset_info",
         "delete_file",
+        "delete_folder",
         "delete_repo",
         "delete_tag",
         "edit_discussion_comment",
@@ -334,6 +335,7 @@ if TYPE_CHECKING:  # pragma: no cover
     from .hf_api import create_tag  # noqa: F401
     from .hf_api import dataset_info  # noqa: F401
     from .hf_api import delete_file  # noqa: F401
+    from .hf_api import delete_folder  # noqa: F401
     from .hf_api import delete_repo  # noqa: F401
     from .hf_api import delete_tag  # noqa: F401
     from .hf_api import edit_discussion_comment  # noqa: F401
diff --git a/src/huggingface_hub/_commit_api.py b/src/huggingface_hub/_commit_api.py
index d66d3e79..1b9a3c20 100644
--- a/src/huggingface_hub/_commit_api.py
+++ b/src/huggingface_hub/_commit_api.py
@@ -35,16 +35,31 @@ CommitOperationT = Union["CommitOperationAdd", "CommitOperationDelete"]
 @dataclass
 class CommitOperationDelete:
     """
-    Data structure holding necessary info to delete
-    a file from a repository on the Hub
+    Data structure holding necessary info to delete a file or a folder from a repository
+    on the Hub.
 
     Args:
         path_in_repo (`str`):
-            Relative filepath in the repo, for example:
-            `"checkpoints/1fec34a/weights.bin"`
+            Relative filepath in the repo, for example: `"checkpoints/1fec34a/weights.bin"`
+            for a file or `"checkpoints/1fec34a/"` for a folder.
+        is_folder (`bool` or `Literal["auto"]`, *optional*)
+            Whether the Delete Operation applies to a folder or not. If "auto", the path
+            type (file or folder) is guessed automatically by looking if path ends with
+            a "/" (folder) or not (file). To explicitly set the path type, you can set
+            `is_folder=True` or `is_folder=False`.
     """
 
     path_in_repo: str
+    is_folder: Union[bool, Literal["auto"]] = "auto"
+
+    def __post_init__(self):
+        if self.is_folder == "auto":
+            self.is_folder = self.path_in_repo.endswith("/")
+        if not isinstance(self.is_folder, bool):
+            raise ValueError(
+                "Wrong value for `is_folder`. Must be one of [`True`, `False`,"
+                f" `'auto'`]. Got '{self.is_folder}'."
+            )
 
 
 @dataclass
@@ -460,6 +475,9 @@ def prepare_commit_payload(
 
     # 4. Send deleted files, one per line
     yield from (
-        {"key": "deletedFile", "value": {"path": del_op.path_in_repo}}
+        {
+            "key": "deletedFolder" if del_op.is_folder else "deletedFile",
+            "value": {"path": del_op.path_in_repo},
+        }
         for del_op in deletions
     )
diff --git a/src/huggingface_hub/_login.py b/src/huggingface_hub/_login.py
index f4968077..b5f3ae3a 100644
--- a/src/huggingface_hub/_login.py
+++ b/src/huggingface_hub/_login.py
@@ -35,7 +35,7 @@ from .utils._deprecation import _deprecate_method
 logger = logging.get_logger(__name__)
 
 
-def login(token: Optional[str] = None, add_to_git_credential: bool = True) -> None:
+def login(token: Optional[str] = None, add_to_git_credential: 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
diff --git a/src/huggingface_hub/file_download.py b/src/huggingface_hub/file_download.py
index 92f041f4..c4334efb 100644
--- a/src/huggingface_hub/file_download.py
+++ b/src/huggingface_hub/file_download.py
@@ -234,7 +234,7 @@ def hf_hub_url(
     return HUGGINGFACE_CO_URL_TEMPLATE.format(
         repo_id=repo_id,
         revision=quote(revision, safe=""),
-        filename=filename,
+        filename=quote(filename),
     )
 
 
diff --git a/src/huggingface_hub/hf_api.py b/src/huggingface_hub/hf_api.py
index 934f5440..a9198ce9 100644
--- a/src/huggingface_hub/hf_api.py
+++ b/src/huggingface_hub/hf_api.py
@@ -22,7 +22,7 @@ from typing import Any, BinaryIO, Dict, Iterable, Iterator, List, Optional, Tupl
 from urllib.parse import quote
 
 import requests
-from huggingface_hub.utils import RepositoryNotFoundError
+from huggingface_hub.utils import EntryNotFoundError, RepositoryNotFoundError
 from requests.exceptions import HTTPError
 
 from ._commit_api import (
@@ -64,7 +64,12 @@ from .utils import (  # noqa: F401 # imported for backward compatibility
     validate_hf_hub_args,
     write_to_credential_store,
 )
-from .utils._deprecation import _deprecate_method, _deprecate_positional_args
+from .utils._deprecation import (
+    _deprecate_arguments,
+    _deprecate_list_output,
+    _deprecate_method,
+    _deprecate_positional_args,
+)
 from .utils._typing import Literal, TypedDict
 from .utils.endpoint_helpers import (
     AttributeDictionary,
@@ -506,7 +511,7 @@ class ModelSearchArguments(AttributeDictionary):
         self._process_models()
 
     def _process_models(self):
-        def clean(s: str):
+        def clean(s: str) -> str:
             return s.replace(" ", "").replace("-", "_").replace(".", "_")
 
         models = self._api.list_models()
@@ -662,6 +667,7 @@ class HfApi:
         d = r.json()
         return DatasetTags(d)
 
+    @_deprecate_list_output(version="0.14")
     @validate_hf_hub_args
     def list_models(
         self,
@@ -674,12 +680,12 @@ class HfApi:
         direction: Optional[Literal[-1]] = None,
         limit: Optional[int] = None,
         full: Optional[bool] = None,
-        cardData: Optional[bool] = None,
-        fetch_config: Optional[bool] = None,
+        cardData: bool = False,
+        fetch_config: bool = False,
         token: Optional[Union[bool, str]] = None,
     ) -> List[ModelInfo]:
         """
-        Get the public list of all the models on huggingface.co
+        Get the list of all the models on huggingface.co
 
         Args:
             filter ([`ModelFilter`] or `str` or `Iterable`, *optional*):
@@ -720,7 +726,10 @@ class HfApi:
                 or [`~huggingface_hub.login`]), token will be retrieved from the cache.
                 If `False`, token is not sent in the request header.
 
-        Returns: List of [`huggingface_hub.hf_api.ModelInfo`] objects
+        Returns:
+            `List[ModelInfo]`: a list of [`huggingface_hub.hf_api.ModelInfo`] objects.
+             To anticipate future pagination, please consider the return value to be a
+             simple iterator.
 
         Example usage with the `filter` argument:
 
@@ -794,10 +803,10 @@ class HfApi:
                 params.update({"full": True})
             elif "full" in params:
                 del params["full"]
-        if fetch_config is not None:
-            params.update({"config": fetch_config})
-        if cardData is not None:
-            params.update({"cardData": cardData})
+        if fetch_config:
+            params.update({"config": True})
+        if cardData:
+            params.update({"cardData": True})
         r = requests.get(path, params=params, headers=headers)
         hf_raise_for_status(r)
         d = r.json()
@@ -874,6 +883,12 @@ class HfApi:
         query_dict["filter"] = tuple(filter_list)
         return query_dict
 
+    @_deprecate_arguments(
+        version="0.14",
+        deprecated_args={"cardData"},
+        custom_message="Use 'full' instead.",
+    )
+    @_deprecate_list_output(version="0.14")
     @validate_hf_hub_args
     def list_datasets(
         self,
@@ -884,12 +899,12 @@ class HfApi:
         sort: Union[Literal["lastModified"], str, None] = None,
         direction: Optional[Literal[-1]] = None,
         limit: Optional[int] = None,
-        cardData: Optional[bool] = None,
+        cardData: Optional[bool] = None,  # deprecated
         full: Optional[bool] = None,
         token: Optional[str] = None,
     ) -> List[DatasetInfo]:
         """
-        Get the public list of all the datasets on huggingface.co
+        Get the list of all the datasets on huggingface.co
 
         Args:
             filter ([`DatasetFilter`] or `str` or `Iterable`, *optional*):
@@ -908,18 +923,21 @@ class HfApi:
             limit (`int`, *optional*):
                 The limit on the number of datasets fetched. Leaving this option
                 to `None` fetches all datasets.
-            cardData (`bool`, *optional*):
-                Whether to grab the metadata for the dataset as well. Can
-                contain useful information such as the PapersWithCode ID.
             full (`bool`, *optional*):
                 Whether to fetch all dataset data, including the `lastModified`
-                and the `cardData`.
+                and the `cardData`. Can contain useful information such as the
+                PapersWithCode ID.
             token (`bool` or `str`, *optional*):
                 A valid authentication token (see https://huggingface.co/settings/token).
                 If `None` or `True` and machine is logged in (through `huggingface-cli login`
                 or [`~huggingface_hub.login`]), token will be retrieved from the cache.
                 If `False`, token is not sent in the request header.
 
+        Returns:
+            `List[DatasetInfo]`: a list of [`huggingface_hub.hf_api.DatasetInfo`] objects.
+             To anticipate future pagination, please consider the return value to be a
+             simple iterator.
+
         Example usage with the `filter` argument:
 
         ```python
@@ -987,12 +1005,8 @@ class HfApi:
             params.update({"direction": direction})
         if limit is not None:
             params.update({"limit": limit})
-        if full is not None:
-            if full:
-                params.update({"full": True})
-        if cardData is not None:
-            if cardData:
-                params.update({"full": True})
+        if full or cardData:
+            params.update({"full": True})
         r = requests.get(path, params=params, headers=headers)
         hf_raise_for_status(r)
         d = r.json()
@@ -1052,6 +1066,7 @@ class HfApi:
         d = r.json()
         return [MetricInfo(**x) for x in d]
 
+    @_deprecate_list_output(version="0.14")
     @validate_hf_hub_args
     def list_spaces(
         self,
@@ -1064,7 +1079,7 @@ class HfApi:
         limit: Optional[int] = None,
         datasets: Union[str, Iterable[str], None] = None,
         models: Union[str, Iterable[str], None] = None,
-        linked: Optional[bool] = None,
+        linked: bool = False,
         full: Optional[bool] = None,
         token: Optional[str] = None,
     ) -> List[SpaceInfo]:
@@ -1105,7 +1120,9 @@ class HfApi:
                 If `False`, token is not sent in the request header.
 
         Returns:
-            `List[SpaceInfo]`: a list of [`huggingface_hub.hf_api.SpaceInfo`] objects
+            `List[SpaceInfo]`: a list of [`huggingface_hub.hf_api.SpaceInfo`] objects.
+             To anticipate future pagination, please consider the return value to be a
+             simple iterator.
         """
         path = f"{self.endpoint}/api/spaces"
         headers = self._build_hf_headers(token=token)
@@ -1122,12 +1139,10 @@ class HfApi:
             params.update({"direction": direction})
         if limit is not None:
             params.update({"limit": limit})
-        if full is not None:
-            if full:
-                params.update({"full": True})
-        if linked is not None:
-            if linked:
-                params.update({"linked": True})
+        if full:
+            params.update({"full": True})
+        if linked:
+            params.update({"linked": True})
         if datasets is not None:
             params.update({"datasets": datasets})
         if models is not None:
@@ -1905,6 +1920,13 @@ class HfApi:
         except RepositoryNotFoundError as e:
             e.append_to_message(_CREATE_COMMIT_NO_REPO_ERROR_MESSAGE)
             raise
+        except EntryNotFoundError as e:
+            if len(deletions) > 0 and "A file with this name doesn't exist" in str(e):
+                e.append_to_message(
+                    "\nMake sure to differentiate file and folder paths in delete"
+                    " operations with a trailing '/' or using `is_folder=True/False`."
+                )
+            raise
 
         commit_data = commit_resp.json()
         return CommitInfo(
@@ -2312,6 +2334,73 @@ class HfApi:
             parent_commit=parent_commit,
         )
 
+    @validate_hf_hub_args
+    def delete_folder(
+        self,
+        path_in_repo: str,
+        repo_id: str,
+        *,
+        token: Optional[str] = None,
+        repo_type: Optional[str] = None,
+        revision: Optional[str] = None,
+        commit_message: Optional[str] = None,
+        commit_description: Optional[str] = None,
+        create_pr: Optional[bool] = None,
+        parent_commit: Optional[str] = None,
+    ) -> CommitInfo:
+        """
+        Deletes a folder in the given repo.
+
+        Simple wrapper around [`create_commit`] method.
+
+        Args:
+            path_in_repo (`str`):
+                Relative folder path in the repo, for example: `"checkpoints/1fec34a"`.
+            repo_id (`str`):
+                The repository from which the folder will be deleted, for example:
+                `"username/custom_transformers"`
+            token (`str`, *optional*):
+                Authentication token, obtained with `HfApi.login` method. Will default
+                to the stored token.
+            repo_type (`str`, *optional*):
+                Set to `"dataset"` or `"space"` if the folder is in a dataset or
+                space, `None` or `"model"` if in a model. Default is `None`.
+            revision (`str`, *optional*):
+                The git revision to commit from. Defaults to the head of the `"main"`
+                branch.
+            commit_message (`str`, *optional*):
+                The summary / title / first line of the generated commit. Defaults to
+                `f"Delete folder {path_in_repo} with huggingface_hub"`.
+            commit_description (`str` *optional*)
+                The description of the generated commit.
+            create_pr (`boolean`, *optional*):
+                Whether or not to create a Pull Request from `revision` with the changes.
+                Defaults to `False`.
+            parent_commit (`str`, *optional*):
+                The OID / SHA of the parent commit, as a hexadecimal string. Shorthands (7 first characters) are also supported.
+                If specified and `create_pr` is `False`, the commit will fail if `revision` does not point to `parent_commit`.
+                If specified and `create_pr` is `True`, the pull request will be created from `parent_commit`.
+                Specifying `parent_commit` ensures the repo has not changed before committing the changes, and can be
+                especially useful if the repo is updated / committed to concurrently.
+        """
+        return self.create_commit(
+            repo_id=repo_id,
+            repo_type=repo_type,
+            token=token,
+            operations=[
+                CommitOperationDelete(path_in_repo=path_in_repo, is_folder=True)
+            ],
+            revision=revision,
+            commit_message=(
+                commit_message
+                if commit_message is not None
+                else f"Delete folder {path_in_repo} with huggingface_hub"
+            ),
+            commit_description=commit_description,
+            create_pr=create_pr,
+            parent_commit=parent_commit,
+        )
+
     @validate_hf_hub_args
     def create_tag(
         self,
@@ -3297,6 +3386,7 @@ move_repo = api.move_repo
 upload_file = api.upload_file
 upload_folder = api.upload_folder
 delete_file = api.delete_file
+delete_folder = api.delete_folder
 create_tag = api.create_tag
 delete_tag = api.delete_tag
 get_full_repo_name = api.get_full_repo_name
diff --git a/src/huggingface_hub/repocard.py b/src/huggingface_hub/repocard.py
index b7cbc25a..61229fc6 100644
--- a/src/huggingface_hub/repocard.py
+++ b/src/huggingface_hub/repocard.py
@@ -27,6 +27,7 @@ from huggingface_hub.utils import is_jinja_available, yaml_dump
 
 from .constants import REPOCARD_NAME
 from .utils import EntryNotFoundError, validate_hf_hub_args
+from .utils._deprecation import _deprecate_positional_args
 from .utils.logging import get_logger
 
 
@@ -547,7 +548,9 @@ def metadata_save(local_path: Union[str, Path], data: Dict) -> None:
         readme.close()
 
 
+@_deprecate_positional_args(version="0.12")
 def metadata_eval_result(
+    *,
     model_pretty_name: str,
     task_pretty_name: str,
     task_id: str,
@@ -561,6 +564,7 @@ def metadata_eval_result(
     dataset_config: Optional[str] = None,
     dataset_split: Optional[str] = None,
     dataset_revision: Optional[str] = None,
+    metrics_verification_token: Optional[str] = None,
 ) -> Dict:
     """
     Creates a metadata dict with the result from a model evaluated on a dataset.
@@ -586,9 +590,7 @@ def metadata_eval_result(
             The name of the metric configuration used in `load_metric()`.
             Example: bleurt-large-512 in `load_metric("bleurt", "bleurt-large-512")`.
         metrics_verified (`bool`, *optional*, defaults to `False`):
-            If true, indicates that evaluation was generated by Hugging Face (vs. self-reported).
-            If a user tries to push self-reported metric results with verified=True, the push
-            will be rejected.
+            Indicates whether the metrics originate from Hugging Face's [evaluation service](https://huggingface.co/spaces/autoevaluate/model-evaluator) or not. Automatically computed by Hugging Face, do not set.
         dataset_config (`str`, *optional*):
             Example: fr. The name of the dataset configuration used in `load_dataset()`.
         dataset_split (`str`, *optional*):
@@ -596,6 +598,8 @@ def metadata_eval_result(
         dataset_revision (`str`, *optional*):
             Example: 5503434ddd753f426f4b38109466949a1217c2bb. The name of the dataset dataset revision
             used in `load_dataset()`.
+        metrics_verification_token (`bool`, *optional*):
+            A JSON Web Token that is used to verify whether the metrics originate from Hugging Face's [evaluation service](https://huggingface.co/spaces/autoevaluate/model-evaluator) or not.
 
     Returns:
         `dict`: a metadata dict with the result from a model evaluated on a dataset.
@@ -663,6 +667,7 @@ def metadata_eval_result(
                     dataset_type=dataset_id,
                     metric_config=metrics_config,
                     verified=metrics_verified,
+                    verify_token=metrics_verification_token,
                     dataset_config=dataset_config,
                     dataset_split=dataset_split,
                     dataset_revision=dataset_revision,
@@ -773,6 +778,9 @@ def metadata_update(
 
     for key, value in metadata.items():
         if key == "model-index":
+            # if the new metadata doesn't include a name, either use existing one or repo name
+            if "name" not in value[0]:
+                value[0]["name"] = getattr(card, "model_name", repo_id)
             model_name, new_results = model_index_to_eval_results(value)
             if card.data.eval_results is None:
                 card.data.eval_results = new_results
diff --git a/src/huggingface_hub/repocard_data.py b/src/huggingface_hub/repocard_data.py
index 213fe37a..87873e85 100644
--- a/src/huggingface_hub/repocard_data.py
+++ b/src/huggingface_hub/repocard_data.py
@@ -51,7 +51,9 @@ class EvalResult:
         metric_args (`Dict[str, Any]`, *optional*):
             The arguments passed during `Metric.compute()`. Example for `bleu`: max_order: 4
         verified (`bool`, *optional*):
-            If true, indicates that evaluation was generated by Hugging Face (vs. self-reported).
+            Indicates whether the metrics originate from Hugging Face's [evaluation service](https://huggingface.co/spaces/autoevaluate/model-evaluator) or not. Automatically computed by Hugging Face, do not set.
+        verify_token (`str`, *optional*):
+            A JSON Web Token that is used to verify whether the metrics originate from Hugging Face's [evaluation service](https://huggingface.co/spaces/autoevaluate/model-evaluator) or not.
     """
 
     # Required
@@ -113,9 +115,12 @@ class EvalResult:
     # Example for `bleu`: max_order: 4
     metric_args: Optional[Dict[str, Any]] = None
 
-    # If true, indicates that evaluation was generated by Hugging Face (vs. self-reported).
+    # Indicates whether the metrics originate from Hugging Face's [evaluation service](https://huggingface.co/spaces/autoevaluate/model-evaluator) or not. Automatically computed by Hugging Face, do not set.
     verified: Optional[bool] = None
 
+    # A JSON Web Token that is used to verify whether the metrics originate from Hugging Face's [evaluation service](https://huggingface.co/spaces/autoevaluate/model-evaluator) or not.
+    verify_token: Optional[str] = None
+
 
 @dataclass
 class CardData:
@@ -183,11 +188,12 @@ class ModelCardData(CardData):
             at https://hf.co/metrics. Example: 'accuracy'. Defaults to None.
         eval_results (`Union[List[EvalResult], EvalResult]`, *optional*):
             List of `huggingface_hub.EvalResult` that define evaluation results of the model. If provided,
-            `model_name` kwarg must be provided. Defaults to `None`.
+            `model_name` is used to as a name on PapersWithCode's leaderboards. Defaults to `None`.
         model_name (`str`, *optional*):
-            A name for this model. Required if you provide `eval_results`. It is used along with
+            A name for this model. It is used along with
             `eval_results` to construct the `model-index` within the card's metadata. The name
-            you supply here is what will be used on PapersWithCode's leaderboards. Defaults to None.
+            you supply here is what will be used on PapersWithCode's leaderboards. If None is provided
+            then the repo name is used as a default. Defaults to None.
         kwargs (`dict`, *optional*):
             Additional metadata that will be added to the model card. Defaults to None.
 
@@ -416,6 +422,7 @@ def model_index_to_eval_results(
                 metric_args = metric.get("args")
                 metric_config = metric.get("config")
                 verified = metric.get("verified")
+                verify_token = metric.get("verifyToken")
 
                 eval_result = EvalResult(
                     task_type=task_type,  # Required
@@ -432,6 +439,7 @@ def model_index_to_eval_results(
                     metric_args=metric_args,
                     metric_config=metric_config,
                     verified=verified,
+                    verify_token=verify_token,
                 )
                 eval_results.append(eval_result)
     return name, eval_results
@@ -521,6 +529,7 @@ def eval_results_to_model_index(
                     "config": result.metric_config,
                     "args": result.metric_args,
                     "verified": result.verified,
+                    "verifyToken": result.verify_token,
                 }
                 for result in results
             ],
diff --git a/src/huggingface_hub/templates/modelcard_template.md b/src/huggingface_hub/templates/modelcard_template.md
index e6f8ab80..d65cd201 100644
--- a/src/huggingface_hub/templates/modelcard_template.md
+++ b/src/huggingface_hub/templates/modelcard_template.md
@@ -73,7 +73,7 @@
 
 <!-- This section is meant to convey recommendations with respect to the bias, risk, and technical limitations. -->
 
-{{ bias_recommendations | default("Users (both direct and downstream) should be made aware of the risks, biases and limitations of the model. More information needed for further recomendations.", true)}}
+{{ bias_recommendations | default("Users (both direct and downstream) should be made aware of the risks, biases and limitations of the model. More information needed for further recommendations.", true)}}
 
 # Training Details
 
diff --git a/src/huggingface_hub/utils/_deprecation.py b/src/huggingface_hub/utils/_deprecation.py
index 6d1582f9..68a14b11 100644
--- a/src/huggingface_hub/utils/_deprecation.py
+++ b/src/huggingface_hub/utils/_deprecation.py
@@ -1,7 +1,7 @@
 import warnings
 from functools import wraps
 from inspect import Parameter, signature
-from typing import Iterable, Optional
+from typing import Generator, Iterable, Optional
 
 
 def _deprecate_positional_args(*, version: str):
@@ -131,3 +131,103 @@ def _deprecate_method(*, version: str, message: Optional[str] = None):
         return inner_f
 
     return _inner_deprecate_method
+
+
+def _deprecate_list_output(*, version: str):
+    """Decorator to deprecate the usage as a list of the output of a method.
+
+    To be used when a method currently returns a list of objects but is planned to return
+    an generator instead in the future. Output is still a list but tweaked to issue a
+    warning message when it is specifically used as a list (e.g. get/set/del item, get
+    length,...).
+
+    Args:
+        version (`str`):
+            The version when output will start to be an generator.
+    """
+
+    def _inner_deprecate_method(f):
+        @wraps(f)
+        def inner_f(*args, **kwargs):
+            list_value = f(*args, **kwargs)
+            return DeprecatedList(
+                list_value,
+                warning_message=(
+                    "'{f.__name__}' currently returns a list of objects but is planned"
+                    " to be a generator starting from version {version} in order to"
+                    " implement pagination. Please avoid to use"
+                    " `{f.__name__}(...).{attr_name}` or explicitly convert the output"
+                    " to a list first with `list(iter({f.__name__})(...))`.".format(
+                        f=f,
+                        version=version,
+                        # Dumb but working workaround to render `attr_name` later
+                        # Taken from https://stackoverflow.com/a/35300723
+                        attr_name="{attr_name}",
+                    )
+                ),
+            )
+
+        return inner_f
+
+    return _inner_deprecate_method
+
+
+def _empty_gen() -> Generator:
+    # Create an empty generator
+    # Taken from https://stackoverflow.com/a/13243870
+    return
+    yield
+
+
+# Build the set of attributes that are specific to a List object (and will be deprecated)
+_LIST_ONLY_ATTRS = frozenset(set(dir([])) - set(dir(_empty_gen())))
+
+
+class DeprecateListMetaclass(type):
+    """Metaclass that overwrites all list-only methods, including magic ones."""
+
+    def __new__(cls, clsname, bases, attrs):
+        # Check consistency
+        if "_deprecate" not in attrs:
+            raise TypeError(
+                "A `_deprecate` method must be implemented to use"
+                " `DeprecateListMetaclass`."
+            )
+        if list not in bases:
+            raise TypeError(
+                "Class must inherit from `list` to use `DeprecateListMetaclass`."
+            )
+
+        # Create decorator to deprecate list-only methods, including magic ones
+        def _with_deprecation(f, name):
+            @wraps(f)
+            def _inner(self, *args, **kwargs):
+                self._deprecate(name)  # Use the `_deprecate`
+                return f(self, *args, **kwargs)
+
+            return _inner
+
+        # Deprecate list-only methods
+        for attr in _LIST_ONLY_ATTRS:
+            attrs[attr] = _with_deprecation(getattr(list, attr), attr)
+
+        return super().__new__(cls, clsname, bases, attrs)
+
+
+class DeprecatedList(list, metaclass=DeprecateListMetaclass):
+    """Custom List class for which all calls to a list-specific method is deprecated.
+
+    Methods that are shared with a generator are not deprecated.
+    See `_deprecate_list_output` for more details.
+    """
+
+    def __init__(self, iterable, warning_message: str):
+        """Initialize the list with a default warning message.
+
+        Warning message will be formatted at runtime with a "{attr_name}" value.
+        """
+        super().__init__(iterable)
+        self._deprecation_msg = warning_message
+
+    def _deprecate(self, attr_name: str) -> None:
+        warnings.warn(self._deprecation_msg.format(attr_name=attr_name), FutureWarning)


Test patch:
diff --git a/tests/test_commit_api.py b/tests/test_commit_api.py
new file mode 100644
index 00000000..1da4e2ca
--- /dev/null
+++ b/tests/test_commit_api.py
@@ -0,0 +1,48 @@
+import unittest
+
+from huggingface_hub._commit_api import CommitOperationDelete
+
+
+class TestCommitOperationDelete(unittest.TestCase):
+    def test_implicit_file(self):
+        self.assertFalse(CommitOperationDelete(path_in_repo="path/to/file").is_folder)
+        self.assertFalse(
+            CommitOperationDelete(path_in_repo="path/to/file.md").is_folder
+        )
+
+    def test_implicit_folder(self):
+        self.assertTrue(CommitOperationDelete(path_in_repo="path/to/folder/").is_folder)
+        self.assertTrue(
+            CommitOperationDelete(path_in_repo="path/to/folder.md/").is_folder
+        )
+
+    def test_explicit_file(self):
+        # Weird case: if user explicitly set as file (`is_folder`=False) but path has a
+        # trailing "/" => user input has priority
+        self.assertFalse(
+            CommitOperationDelete(
+                path_in_repo="path/to/folder/", is_folder=False
+            ).is_folder
+        )
+        self.assertFalse(
+            CommitOperationDelete(
+                path_in_repo="path/to/folder.md/", is_folder=False
+            ).is_folder
+        )
+
+    def test_explicit_folder(self):
+        # No need for the trailing "/" is `is_folder` explicitly passed
+        self.assertTrue(
+            CommitOperationDelete(
+                path_in_repo="path/to/folder", is_folder=True
+            ).is_folder
+        )
+        self.assertTrue(
+            CommitOperationDelete(
+                path_in_repo="path/to/folder.md", is_folder=True
+            ).is_folder
+        )
+
+    def test_is_folder_wrong_value(self):
+        with self.assertRaises(ValueError):
+            CommitOperationDelete(path_in_repo="path/to/folder", is_folder="any value")
diff --git a/tests/test_file_download.py b/tests/test_file_download.py
index 9ae86fb4..aa029a80 100644
--- a/tests/test_file_download.py
+++ b/tests/test_file_download.py
@@ -397,6 +397,49 @@ class StagingCachedDownloadTest(unittest.TestCase):
                 )
 
 
+@pytest.mark.usefixtures("fx_cache_dir")
+class StagingCachedDownloadOnAwfulFilenamesTest(unittest.TestCase):
+    """Implement regression tests for #1161.
+
+    Issue was on filename not url encoded by `hf_hub_download` and `hf_hub_url`.
+
+    See https://github.com/huggingface/huggingface_hub/issues/1161
+    """
+
+    cache_dir: Path
+    repo_id = "valid_org/repo_with_awful_filename"
+    subfolder = "subfolder/to?"
+    filename = "awful?filename%you:should,never.give"
+    filepath = "subfolder/to?/awful?filename%you:should,never.give"
+    expected_url = "https://hub-ci.huggingface.co/valid_org/repo_with_awful_filename/resolve/main/subfolder/to%3F/awful%3Ffilename%25you%3Ashould%2Cnever.give"
+
+    def test_hf_hub_url_on_awful_filepath(self):
+        self.assertEqual(hf_hub_url(self.repo_id, self.filepath), self.expected_url)
+
+    def test_hf_hub_url_on_awful_subfolder_and_filename(self):
+        self.assertEqual(
+            hf_hub_url(self.repo_id, self.filename, subfolder=self.subfolder),
+            self.expected_url,
+        )
+
+    def test_hf_hub_download_on_awful_filepath(self):
+        local_path = hf_hub_download(
+            self.repo_id, self.filepath, cache_dir=self.cache_dir
+        )
+        # Local path is not url-encoded
+        self.assertTrue(local_path.endswith(self.filepath))
+
+    def test_hf_hub_download_on_awful_subfolder_and_filename(self):
+        local_path = hf_hub_download(
+            self.repo_id,
+            self.filename,
+            subfolder=self.subfolder,
+            cache_dir=self.cache_dir,
+        )
+        # Local path is not url-encoded
+        self.assertTrue(local_path.endswith(self.filepath))
+
+
 class CreateSymlinkTest(unittest.TestCase):
     @patch("huggingface_hub.file_download.are_symlinks_supported")
     def test_create_relative_symlink_concurrent_access(
diff --git a/tests/test_hf_api.py b/tests/test_hf_api.py
index dd36c304..cdb53cfa 100644
--- a/tests/test_hf_api.py
+++ b/tests/test_hf_api.py
@@ -59,6 +59,7 @@ from huggingface_hub.hf_api import (
     repo_type_and_id_from_hf_id,
 )
 from huggingface_hub.utils import (
+    EntryNotFoundError,
     HfFolder,
     HfHubHTTPError,
     RepositoryNotFoundError,
@@ -1024,6 +1025,60 @@ class CommitApiTest(HfApiCommonTestWithLogin):
             self._api.delete_repo(repo_id=REPO_NAME)
 
 
+class HfApiDeleteFolderTest(HfApiCommonTestWithLogin):
+    def setUp(self):
+        self.repo_id = f"{USER}/{repo_name('create_commit_delete_folder')}"
+        self._api.create_repo(repo_id=self.repo_id, exist_ok=False)
+
+        self._api.create_commit(
+            repo_id=self.repo_id,
+            commit_message="Init repo",
+            operations=[
+                CommitOperationAdd(path_or_fileobj=b"data", path_in_repo="1/file_1.md"),
+                CommitOperationAdd(path_or_fileobj=b"data", path_in_repo="1/file_2.md"),
+                CommitOperationAdd(path_or_fileobj=b"data", path_in_repo="2/file_3.md"),
+            ],
+        )
+
+    def tearDown(self):
+        self._api.delete_repo(repo_id=self.repo_id)
+
+    @retry_endpoint
+    def test_create_commit_delete_folder_implicit(self):
+        self._api.create_commit(
+            operations=[CommitOperationDelete(path_in_repo="1/")],
+            commit_message="Test delete folder implicit",
+            repo_id=self.repo_id,
+        )
+
+        with self.assertRaises(EntryNotFoundError):
+            hf_hub_download(self.repo_id, "1/file_1.md", use_auth_token=self._token)
+
+        with self.assertRaises(EntryNotFoundError):
+            hf_hub_download(self.repo_id, "1/file_2.md", use_auth_token=self._token)
+
+        # Still exists
+        hf_hub_download(self.repo_id, "2/file_3.md", use_auth_token=self._token)
+
+    @retry_endpoint
+    def test_create_commit_delete_folder_explicit(self):
+        self._api.delete_folder(path_in_repo="1", repo_id=self.repo_id)
+        with self.assertRaises(EntryNotFoundError):
+            hf_hub_download(self.repo_id, "1/file_1.md", use_auth_token=self._token)
+
+    @retry_endpoint
+    def test_create_commit_failing_implicit_delete_folder(self):
+        with self.assertRaisesRegex(
+            EntryNotFoundError,
+            "Make sure to differentiate file and folder paths",
+        ):
+            self._api.create_commit(
+                operations=[CommitOperationDelete(path_in_repo="1")],
+                commit_message="Failing delete folder",
+                repo_id=self.repo_id,
+            )
+
+
 class HfApiTagEndpointTest(HfApiCommonTestWithLogin):
     _user = USER
     _repo_id: str
@@ -1176,6 +1231,7 @@ class HfApiPublicTest(unittest.TestCase):
         _api = HfApi(endpoint=ENDPOINT_STAGING)
         _ = _api.list_models()
 
+    @expect_deprecation("list_models")
     @with_production_testing
     def test_list_models(self):
         _api = HfApi()
@@ -1183,6 +1239,7 @@ class HfApiPublicTest(unittest.TestCase):
         self.assertGreater(len(models), 100)
         self.assertIsInstance(models[0], ModelInfo)
 
+    @expect_deprecation("list_models")
     @with_production_testing
     def test_list_models_author(self):
         _api = HfApi()
@@ -1192,6 +1249,7 @@ class HfApiPublicTest(unittest.TestCase):
         for model in models:
             self.assertTrue(model.modelId.startswith("google/"))
 
+    @expect_deprecation("list_models")
     @with_production_testing
     def test_list_models_search(self):
         _api = HfApi()
@@ -1204,6 +1262,7 @@ class HfApiPublicTest(unittest.TestCase):
             # (and changes it in the future) but for now it should do the trick.
             self.assertTrue("bert" in model.modelId.lower())
 
+    @expect_deprecation("list_models")
     @with_production_testing
     def test_list_models_complex_query(self):
         # Let's list the 10 most recent models
@@ -1222,15 +1281,17 @@ class HfApiPublicTest(unittest.TestCase):
 
     @with_production_testing
     def test_list_models_with_config(self):
-        _api = HfApi()
-        models = _api.list_models(
+        for model in HfApi().list_models(
             filter="adapter-transformers", fetch_config=True, limit=20
-        )
-        found_configs = 0
-        for model in models:
-            if model.config:
-                found_configs = found_configs + 1
-        self.assertGreater(found_configs, 0)
+        ):
+            self.assertIsNotNone(model.config)
+
+    @with_production_testing
+    def test_list_models_without_config(self):
+        for model in HfApi().list_models(
+            filter="adapter-transformers", fetch_config=False, limit=20
+        ):
+            self.assertIsNone(model.config)
 
     @with_production_testing
     def test_model_info(self):
@@ -1295,13 +1356,15 @@ class HfApiPublicTest(unittest.TestCase):
         _api = HfApi(endpoint=ENDPOINT_STAGING)
         _ = _api.list_datasets()
 
+    @expect_deprecation("list_datasets")
     @with_production_testing
-    def test_list_datasets(self):
+    def test_list_datasets_no_filter(self):
         _api = HfApi()
         datasets = _api.list_datasets()
         self.assertGreater(len(datasets), 100)
         self.assertIsInstance(datasets[0], DatasetInfo)
 
+    @expect_deprecation("list_datasets")
     @with_production_testing
     def test_filter_datasets_by_author_and_name(self):
         _api = HfApi()
@@ -1311,6 +1374,7 @@ class HfApiPublicTest(unittest.TestCase):
         self.assertTrue("huggingface" in datasets[0].author)
         self.assertTrue("DataMeasurementsFiles" in datasets[0].id)
 
+    @expect_deprecation("list_datasets")
     @with_production_testing
     def test_filter_datasets_by_benchmark(self):
         _api = HfApi()
@@ -1319,6 +1383,7 @@ class HfApiPublicTest(unittest.TestCase):
         self.assertGreater(len(datasets), 0)
         self.assertTrue("benchmark:raft" in datasets[0].tags)
 
+    @expect_deprecation("list_datasets")
     @with_production_testing
     def test_filter_datasets_by_language_creator(self):
         _api = HfApi()
@@ -1327,6 +1392,7 @@ class HfApiPublicTest(unittest.TestCase):
         self.assertGreater(len(datasets), 0)
         self.assertTrue("language_creators:crowdsourced" in datasets[0].tags)
 
+    @expect_deprecation("list_datasets")
     @with_production_testing
     def test_filter_datasets_by_language(self):
         _api = HfApi()
@@ -1341,6 +1407,7 @@ class HfApiPublicTest(unittest.TestCase):
         self.assertTrue("languages:en" in datasets[0].tags)
         self.assertTrue("languages:fr" in datasets[0].tags)
 
+    @expect_deprecation("list_datasets")
     @with_production_testing
     def test_filter_datasets_by_multilinguality(self):
         _api = HfApi()
@@ -1349,6 +1416,7 @@ class HfApiPublicTest(unittest.TestCase):
         self.assertGreater(len(datasets), 0)
         self.assertTrue("multilinguality:multilingual" in datasets[0].tags)
 
+    @expect_deprecation("list_datasets")
     @with_production_testing
     def test_filter_datasets_by_size_categories(self):
         _api = HfApi()
@@ -1357,6 +1425,7 @@ class HfApiPublicTest(unittest.TestCase):
         self.assertGreater(len(datasets), 0)
         self.assertTrue("size_categories:100K<n<1M" in datasets[0].tags)
 
+    @expect_deprecation("list_datasets")
     @with_production_testing
     def test_filter_datasets_by_task_categories(self):
         _api = HfApi()
@@ -1365,6 +1434,7 @@ class HfApiPublicTest(unittest.TestCase):
         self.assertGreater(len(datasets), 0)
         self.assertTrue("task_categories:audio-classification" in datasets[0].tags)
 
+    @expect_deprecation("list_datasets")
     @with_production_testing
     def test_filter_datasets_by_task_ids(self):
         _api = HfApi()
@@ -1373,6 +1443,7 @@ class HfApiPublicTest(unittest.TestCase):
         self.assertGreater(len(datasets), 0)
         self.assertTrue("task_ids:automatic-speech-recognition" in datasets[0].tags)
 
+    @expect_deprecation("list_datasets")
     @with_production_testing
     def test_list_datasets_full(self):
         _api = HfApi()
@@ -1382,6 +1453,7 @@ class HfApiPublicTest(unittest.TestCase):
         self.assertIsInstance(dataset, DatasetInfo)
         self.assertTrue(any(dataset.cardData for dataset in datasets))
 
+    @expect_deprecation("list_datasets")
     @with_production_testing
     def test_list_datasets_author(self):
         _api = HfApi()
@@ -1389,6 +1461,7 @@ class HfApiPublicTest(unittest.TestCase):
         self.assertGreater(len(datasets), 1)
         self.assertIsInstance(datasets[0], DatasetInfo)
 
+    @expect_deprecation("list_datasets")
     @with_production_testing
     def test_list_datasets_search(self):
         _api = HfApi()
@@ -1396,6 +1469,7 @@ class HfApiPublicTest(unittest.TestCase):
         self.assertGreater(len(datasets), 10)
         self.assertIsInstance(datasets[0], DatasetInfo)
 
+    @expect_deprecation("list_datasets")
     @with_production_testing
     def test_filter_datasets_with_cardData(self):
         _api = HfApi()
@@ -1465,6 +1539,7 @@ class HfApiPublicTest(unittest.TestCase):
         self.assertIsInstance(metrics[0], MetricInfo)
         self.assertTrue(any(metric.description for metric in metrics))
 
+    @expect_deprecation("list_models")
     @with_production_testing
     def test_filter_models_by_author(self):
         _api = HfApi()
@@ -1473,6 +1548,7 @@ class HfApiPublicTest(unittest.TestCase):
         self.assertGreater(len(models), 0)
         self.assertTrue("muellerzr" in models[0].modelId)
 
+    @expect_deprecation("list_models")
     @with_production_testing
     def test_filter_models_by_author_and_name(self):
         # Test we can search by an author and a name, but the model is not found
@@ -1481,6 +1557,7 @@ class HfApiPublicTest(unittest.TestCase):
         models = _api.list_models(filter=f)
         self.assertTrue("facebook/bart-base" in models[0].modelId)
 
+    @expect_deprecation("list_models")
     @with_production_testing
     def test_failing_filter_models_by_author_and_model_name(self):
         # Test we can search by an author and a name, but the model is not found
@@ -1489,6 +1566,7 @@ class HfApiPublicTest(unittest.TestCase):
         models = _api.list_models(filter=f)
         self.assertEqual(len(models), 0)
 
+    @expect_deprecation("list_models")
     @with_production_testing
     def test_filter_models_with_library(self):
         _api = HfApi()
@@ -1499,6 +1577,7 @@ class HfApiPublicTest(unittest.TestCase):
         models = _api.list_models(filter=f)
         self.assertGreater(len(models), 0)
 
+    @expect_deprecation("list_models")
     @with_production_testing
     def test_filter_models_with_task(self):
         _api = HfApi()
@@ -1510,6 +1589,7 @@ class HfApiPublicTest(unittest.TestCase):
         models = _api.list_models(filter=f)
         self.assertGreater(1, len(models))
 
+    @expect_deprecation("list_models")
     @with_production_testing
     def test_filter_models_by_language(self):
         _api = HfApi()
@@ -1521,6 +1601,7 @@ class HfApiPublicTest(unittest.TestCase):
 
         assert len(res_fr) != len(res_en)
 
+    @expect_deprecation("list_models")
     @with_production_testing
     def test_filter_models_with_complex_query(self):
         _api = HfApi()
@@ -1609,6 +1690,7 @@ class HfApiPublicTest(unittest.TestCase):
             )
         )
 
+    @expect_deprecation("list_spaces")
     @with_production_testing
     def test_list_spaces_full(self):
         _api = HfApi()
@@ -1618,6 +1700,7 @@ class HfApiPublicTest(unittest.TestCase):
         self.assertIsInstance(space, SpaceInfo)
         self.assertTrue(any(space.cardData for space in spaces))
 
+    @expect_deprecation("list_spaces")
     @with_production_testing
     def test_list_spaces_author(self):
         _api = HfApi()
@@ -1629,6 +1712,7 @@ class HfApiPublicTest(unittest.TestCase):
             )
         )
 
+    @expect_deprecation("list_spaces")
     @with_production_testing
     def test_list_spaces_search(self):
         _api = HfApi()
@@ -1636,6 +1720,7 @@ class HfApiPublicTest(unittest.TestCase):
         space = spaces[0]
         self.assertTrue("wikipedia" in space.id.lower())
 
+    @expect_deprecation("list_spaces")
     @with_production_testing
     def test_list_spaces_sort_and_direction(self):
         _api = HfApi()
@@ -1648,18 +1733,21 @@ class HfApiPublicTest(unittest.TestCase):
             spaces_ascending_likes[-2].likes, spaces_ascending_likes[-1].likes
         )
 
+    @expect_deprecation("list_spaces")
     @with_production_testing
     def test_list_spaces_limit(self):
         _api = HfApi()
         spaces = _api.list_spaces(limit=5)
         self.assertEqual(len(spaces), 5)
 
+    @expect_deprecation("list_spaces")
     @with_production_testing
     def test_list_spaces_with_models(self):
         _api = HfApi()
         spaces = _api.list_spaces(models="bert-base-uncased")
         self.assertTrue("bert-base-uncased" in getattr(spaces[0], "models", []))
 
+    @expect_deprecation("list_spaces")
     @with_production_testing
     def test_list_spaces_with_datasets(self):
         _api = HfApi()
@@ -1740,16 +1828,19 @@ class HfApiPrivateTest(HfApiCommonTestWithLogin):
             )
             self.assertIsInstance(dataset_info, DatasetInfo)
 
+    @expect_deprecation("list_datasets")
     def test_list_private_datasets(self):
         orig = len(self._api.list_datasets(use_auth_token=False))
         new = len(self._api.list_datasets(use_auth_token=self._token))
         self.assertGreater(new, orig)
 
+    @expect_deprecation("list_models")
     def test_list_private_models(self):
         orig = len(self._api.list_models(use_auth_token=False))
         new = len(self._api.list_models(use_auth_token=self._token))
         self.assertGreater(new, orig)
 
+    @expect_deprecation("list_spaces")
     @with_production_testing
     def test_list_private_spaces(self):
         orig = len(self._api.list_spaces(use_auth_token=False))
diff --git a/tests/test_repocard.py b/tests/test_repocard.py
index 87745620..0e759d26 100644
--- a/tests/test_repocard.py
+++ b/tests/test_repocard.py
@@ -420,6 +420,50 @@ class RepocardMetadataUpdateTest(unittest.TestCase):
         expected_metadata = {"license": "cc-by-sa-4.0", "tag": "test"}
         self.assertDictEqual(updated_metadata, expected_metadata)
 
+    def test_update_with_existing_name(self):
+        new_metadata = copy.deepcopy(self.existing_metadata)
+        new_metadata["model-index"][0].pop("name")
+        new_metadata["model-index"][0]["results"][0]["metrics"][0][
+            "value"
+        ] = 0.2862102282047272
+
+        metadata_update(
+            f"{USER}/{self.REPO_NAME}",
+            new_metadata,
+            token=self._token,
+            overwrite=True,
+        )
+
+        card_data = ModelCard.load(f"{USER}/{self.REPO_NAME}", token=self._token)
+
+        self.assertEqual(
+            card_data.data.model_name, self.existing_metadata["model-index"][0]["name"]
+        )
+
+    def test_update_without_existing_name(self):
+
+        # delete existing metadata
+        self._api.upload_file(
+            path_or_fileobj="# Test".encode(),
+            repo_id=f"{USER}/{self.REPO_NAME}",
+            path_in_repo="README.md",
+            commit_message="Add README to main branch",
+        )
+
+        new_metadata = copy.deepcopy(self.existing_metadata)
+        new_metadata["model-index"][0].pop("name")
+
+        metadata_update(
+            f"{USER}/{self.REPO_NAME}",
+            new_metadata,
+            token=self._token,
+            overwrite=True,
+        )
+
+        card_data = ModelCard.load(f"{USER}/{self.REPO_NAME}", token=self._token)
+
+        self.assertEqual(card_data.data.model_name, f"{USER}/{self.REPO_NAME}")
+
 
 class TestMetadataUpdateOnMissingCard(unittest.TestCase):
     def setUp(self) -> None:
diff --git a/tests/test_repocard_data.py b/tests/test_repocard_data.py
index 514345b5..3f57e279 100644
--- a/tests/test_repocard_data.py
+++ b/tests/test_repocard_data.py
@@ -91,6 +91,8 @@ class ModelCardDataTest(unittest.TestCase):
                             {
                                 "type": "acc",
                                 "value": 0.9,
+                                "verified": True,
+                                "verifyToken": 1234,
                             }
                         ],
                     },
@@ -106,6 +108,8 @@ class ModelCardDataTest(unittest.TestCase):
         self.assertEqual(eval_results[1].metric_value, 0.9)
         self.assertEqual(eval_results[2].task_type, "image-classification")
         self.assertEqual(eval_results[2].dataset_type, "beans")
+        self.assertEqual(eval_results[2].verified, True)
+        self.assertEqual(eval_results[2].verify_token, 1234)
 
     def test_card_data_requires_model_name_for_eval_results(self):
         with pytest.raises(
diff --git a/tests/test_utils_deprecation.py b/tests/test_utils_deprecation.py
index ccaf6f78..9dadfc8b 100644
--- a/tests/test_utils_deprecation.py
+++ b/tests/test_utils_deprecation.py
@@ -5,6 +5,7 @@ import pytest
 
 from huggingface_hub.utils._deprecation import (
     _deprecate_arguments,
+    _deprecate_list_output,
     _deprecate_method,
     _deprecate_positional_args,
 )
@@ -127,3 +128,61 @@ class TestDeprecationUtils(unittest.TestCase):
             "'dummy_deprecated' (from 'tests.test_utils_deprecation') is deprecated"
             " and will be removed from version 'xxx'. This is a custom message.",
         )
+
+    def test_deprecate_list_output(self) -> None:
+        """Test test_deprecate_list_output throw warnings."""
+
+        @_deprecate_list_output(version="xxx")
+        def dummy_deprecated() -> None:
+            return [1, 2, 3]
+
+        output = dummy_deprecated()
+
+        # Still a list !
+        self.assertIsInstance(output, list)
+
+        # __getitem__
+        with pytest.warns(FutureWarning) as record:
+            self.assertEqual(output[0], 1)
+
+        # (check real message once)
+        self.assertEqual(
+            record[0].message.args[0],
+            "'dummy_deprecated' currently returns a list of objects but is planned to"
+            " be a generator starting from version xxx in order to implement"
+            " pagination. Please avoid to use `dummy_deprecated(...).__getitem__` or"
+            " explicitly convert the output to a list first with"
+            " `list(iter(dummy_deprecated)(...))`.",
+        )
+
+        # __setitem__
+        with pytest.warns(FutureWarning):
+            output[0] = 10
+
+        # __delitem__
+        with pytest.warns(FutureWarning):
+            del output[1]
+
+        # `.append` deprecated (as .index, .pop, .insert, .remove, .sort,...)
+        with pytest.warns(FutureWarning):
+            output.append(5)
+
+        # Magic __len__ deprecated (as __add__, __mul__, __contains__,...)
+        with pytest.warns(FutureWarning):
+            self.assertEqual(len(output), 3)
+
+        with pytest.warns(FutureWarning):
+            self.assertTrue(3 in output)
+
+        # List has been modified
+        # Iterating over items is NOT deprecated !
+        for item, expected in zip(output, [10, 3, 5]):
+            self.assertEqual(item, expected)
+
+        # If output is converted to list, no warning is raised.
+        output_as_list = list(iter(dummy_deprecated()))
+        self.assertEqual(output_as_list[0], 1)
+        del output_as_list[1]
+        output_as_list.append(5)
+        self.assertEqual(len(output_as_list), 3)
+        self.assertTrue(3 in output_as_list)