Skip to content

Commit

Permalink
Add list_repo_likers method to HfApi (#1715)
Browse files Browse the repository at this point in the history
* update list_repo_likers function

Signed-off-by: Issam Arabi <issam@cs.toronto.edu>

* add test for list_repo_likers

* Update list_repo_likers description

Co-authored-by: Lucain <lucainp@gmail.com>

* optimize list_repo_likers implementation

Co-authored-by: Lucain <lucainp@gmail.com>

* make style + import at root

---------

Signed-off-by: Issam Arabi <issam@cs.toronto.edu>
Co-authored-by: Lucain <lucainp@gmail.com>
  • Loading branch information
issamarabi and Wauplin committed Oct 6, 2023
1 parent 5d2d297 commit 35117fb
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 0 deletions.
4 changes: 4 additions & 0 deletions docs/source/en/package_reference/hf_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ models = hf_api.list_models()

[[autodoc]] huggingface_hub.hf_api.SpaceInfo

### User

[[autodoc]] huggingface_hub.hf_api.User

### UserLikes

[[autodoc]] huggingface_hub.hf_api.UserLikes
Expand Down
4 changes: 4 additions & 0 deletions src/huggingface_hub/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@
"HfApi",
"ModelSearchArguments",
"RepoUrl",
"User",
"UserLikes",
"add_collection_item",
"add_space_secret",
Expand Down Expand Up @@ -188,6 +189,7 @@
"list_models",
"list_repo_commits",
"list_repo_files",
"list_repo_likers",
"list_repo_refs",
"list_spaces",
"merge_pull_request",
Expand Down Expand Up @@ -462,6 +464,7 @@ def __dir__():
HfApi, # noqa: F401
ModelSearchArguments, # noqa: F401
RepoUrl, # noqa: F401
User, # noqa: F401
UserLikes, # noqa: F401
add_collection_item, # noqa: F401
add_space_secret, # noqa: F401
Expand Down Expand Up @@ -507,6 +510,7 @@ def __dir__():
list_models, # noqa: F401
list_repo_commits, # noqa: F401
list_repo_files, # noqa: F401
list_repo_likers, # noqa: F401
list_repo_refs, # noqa: F401
list_spaces, # noqa: F401
merge_pull_request, # noqa: F401
Expand Down
71 changes: 71 additions & 0 deletions src/huggingface_hub/hf_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,26 @@ class UserLikes:
spaces: List[str]


@dataclass
class User:
"""
Contains information about a user on the Hub.
Args:
avatar_url (`str`):
URL of the user's avatar.
username (`str`):
Name of the user on the Hub (unique).
fullname (`str`):
User's full name.
"""

# Metadata
avatar_url: str
username: str
fullname: str


def future_compatible(fn: CallableT) -> CallableT:
"""Wrap a method of `HfApi` to handle `run_as_future=True`.
Expand Down Expand Up @@ -1730,6 +1750,56 @@ def list_liked_repos(
spaces=[like["repo"]["name"] for like in likes if like["repo"]["type"] == "space"],
)

@validate_hf_hub_args
def list_repo_likers(
self,
repo_id: str,
*,
repo_type: Optional[str] = None,
token: Optional[str] = None,
) -> List[User]:
"""
List all users who liked a given repo on the hugging Face Hub.
See also [`like`] and [`list_liked_repos`].
Args:
repo_id (`str`):
The repository to retrieve . Example: `"user/my-cool-model"`.
token (`str`, *optional*):
Authentication token. Will default to the stored token.
repo_type (`str`, *optional*):
Set to `"dataset"` or `"space"` if uploading to a dataset or
space, `None` or `"model"` if uploading to a model. Default is
`None`.
Returns:
`List[User]`: a list of [`User`] objects.
"""

# Construct the API endpoint
if repo_type is None:
repo_type = REPO_TYPE_MODEL
path = f"{self.endpoint}/api/{repo_type}s/{repo_id}/likers"
headers = self._build_hf_headers(token=token)

# Make the request
response = get_session().get(path, headers=headers)
hf_raise_for_status(response)

# Parse the results into User objects
likers_data = response.json()
return [
User(
username=user_data["user"],
fullname=user_data["fullname"],
avatar_url=user_data["avatarUrl"],
)
for user_data in likers_data
]

@validate_hf_hub_args
def model_info(
self,
Expand Down Expand Up @@ -6475,6 +6545,7 @@ def _parse_revision_from_pr_url(pr_url: str) -> str:

# Activity API
list_liked_repos = api.list_liked_repos
list_repo_likers = api.list_repo_likers
like = api.like
unlike = api.unlike

Expand Down
16 changes: 16 additions & 0 deletions tests/test_hf_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2467,6 +2467,22 @@ def test_list_likes_repos_auth_and_explicit_user(self) -> None:
likes = self.api.list_liked_repos(user="__DUMMY_DATASETS_SERVER_USER__", token=TOKEN)
self.assertEqual(likes.user, "__DUMMY_DATASETS_SERVER_USER__")

def test_list_repo_likers(self) -> None:
# Create a repo + like
repo_id = self.api.create_repo(repo_name(), token=TOKEN).repo_id
self.api.like(repo_id, token=TOKEN)

# Use list_repo_likers to get the list of users who liked this repo
likers = self.api.list_repo_likers(repo_id, token=TOKEN)

# Check if the test user is in the list of likers
liker_usernames = [user.username for user in likers]
self.assertGreater(len(likers), 0)
self.assertIn(USER, liker_usernames)

# Cleanup
self.api.delete_repo(repo_id, token=TOKEN)

@with_production_testing
def test_list_likes_on_production(self) -> None:
# Test julien-c likes a lot of repos !
Expand Down

0 comments on commit 35117fb

Please sign in to comment.