Repository: huggingface/huggingface_hub
Pull request #41 (2021-05-07 08:49:24)

Document the `filter` argument
This documents the filter argument.

julien-c: also cc'ing @osanseviero @thomwolf and @Pierrci 
osanseviero: LGTM! :) if possible I would take the opportunity to also document the other arguments (not sure what `full` is or how to use it)

Patch:
diff --git a/setup.py b/setup.py
index e91e00fc..e7dad993 100644
--- a/setup.py
+++ b/setup.py
@@ -15,6 +15,7 @@ install_requires = [
     "filelock",
     "requests",
     "tqdm",
+    "typing-extensions",
     "importlib_metadata;python_version<'3.8'",
 ]
 
diff --git a/src/huggingface_hub/hf_api.py b/src/huggingface_hub/hf_api.py
index f59af85d..9c9650b8 100644
--- a/src/huggingface_hub/hf_api.py
+++ b/src/huggingface_hub/hf_api.py
@@ -16,10 +16,17 @@
 
 import os
 import re
+import sys
 import warnings
 from io import BufferedIOBase, RawIOBase
 from os.path import expanduser
-from typing import BinaryIO, Dict, List, Optional, Tuple, Union
+from typing import BinaryIO, Dict, Iterable, List, Optional, Tuple, Union
+
+
+if sys.version_info >= (3, 8):
+    from typing import Literal
+else:
+    from typing_extensions import Literal
 
 import requests
 from requests.exceptions import HTTPError
@@ -63,6 +70,7 @@ class ModelInfo:
         self,
         modelId: Optional[str] = None,  # id of model
         sha: Optional[str] = None,  # commit sha at the specified revision
+        lastModified: Optional[str] = None,  # date of last commit to repo
         tags: List[str] = [],
         pipeline_tag: Optional[str] = None,
         siblings: Optional[
@@ -72,6 +80,7 @@ class ModelInfo:
     ):
         self.modelId = modelId
         self.sha = sha
+        self.lastModified = lastModified
         self.tags = tags
         self.pipeline_tag = pipeline_tag
         self.siblings = (
@@ -129,12 +138,68 @@ class HfApi:
         r = requests.post(path, headers={"authorization": "Bearer {}".format(token)})
         r.raise_for_status()
 
-    def list_models(self, filter: Optional[str] = None) -> List[ModelInfo]:
+    def list_models(
+        self,
+        filter: Union[str, Iterable[str], None] = None,
+        sort: Union[Literal["lastModified"], str, None] = None,
+        direction: Optional[Literal[-1]] = None,
+        limit: Optional[int] = None,
+        full: Optional[bool] = None,
+    ) -> List[ModelInfo]:
         """
         Get the public list of all the models on huggingface.co
+
+        Args:
+            filter (:obj:`str` or :class:`Iterable`, `optional`):
+                A string which can be used to identify models on the hub by their tags.
+                Example usage:
+
+                    >>> from huggingface_hub import HfApi
+                    >>> api = HfApi()
+
+                    >>> # List all models
+                    >>> api.list_models()
+
+                    >>> # List only the text classification models
+                    >>> api.list_models(filter="text-classification")
+
+                    >>> # List only the russian models compatible with pytorch
+                    >>> api.list_models(filter=("ru", "pytorch"))
+
+                    >>> # List only the models trained on the "common_voice" dataset
+                    >>> api.list_models(filter="dataset:common_voice")
+
+                    >>> # List only the models from the AllenNLP library
+                    >>> api.list_models(filter="allennlp")
+            sort (:obj:`Literal["lastModified"]` or :obj:`str`, `optional`):
+                The key with which to sort the resulting models. Possible values are the properties of the `ModelInfo`
+                class.
+            direction (:obj:`Literal[-1]` or :obj:`int`, `optional`):
+                Direction in which to sort. The value `-1` sorts by descending order while all other values
+                sort by ascending order.
+            limit (:obj:`int`, `optional`):
+                The limit on the number of models fetched. Leaving this option to `None` fetches all models.
+            full (:obj:`bool`, `optional`):
+                Whether to fetch all model data, including the `lastModified`, the `sha`, the files and the `tags`.
+                This is set to `True` by default when using a filter.
+
         """
         path = "{}/api/models".format(self.endpoint)
-        params = {"filter": filter, "full": True} if filter is not None else None
+        params = {}
+        if filter is not None:
+            params.update({"filter": filter})
+            params.update({"full": True})
+        if sort is not None:
+            params.update({"sort": sort})
+        if direction is not None:
+            params.update({"direction": direction})
+        if limit is not None:
+            params.update({"limit": limit})
+        if full is not None:
+            if full:
+                params.update({"full": True})
+            elif "full" in params:
+                del params["full"]
         r = requests.get(path, params=params)
         r.raise_for_status()
         d = r.json()


Test patch:
diff --git a/tests/test_hf_api.py b/tests/test_hf_api.py
index 96a0f27c..f6fa3d6d 100644
--- a/tests/test_hf_api.py
+++ b/tests/test_hf_api.py
@@ -230,6 +230,21 @@ class HfApiPublicTest(unittest.TestCase):
         self.assertGreater(len(models), 100)
         self.assertIsInstance(models[0], ModelInfo)
 
+    def test_list_models_complex_query(self):
+        # Let's list the 10 most recent models
+        # with tags "bert" and "jax",
+        # ordered by last modified date.
+        _api = HfApi()
+        models = _api.list_models(
+            filter=("bert", "jax"), sort="lastModified", direction=-1, limit=10
+        )
+        # we have at least 1 models
+        self.assertGreater(len(models), 1)
+        self.assertLessEqual(len(models), 10)
+        model = models[0]
+        self.assertIsInstance(model, ModelInfo)
+        self.assertTrue(all(tag in model.tags for tag in ["bert", "jax"]))
+
     def test_model_info(self):
         _api = HfApi()
         model = _api.model_info(repo_id=DUMMY_MODEL_ID)