Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add list_repo_likers method to HfApi #1715

Merged
merged 5 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 90 additions & 12 deletions src/huggingface_hub/hf_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,31 @@ class UserLikes:
spaces: List[str]


@dataclass
class User:
"""
Contains information about a user on the Hub.

Args:
name (`str`):
Name of the user.
avatarUrl (`str`):
URL of the user's avatar.
fullName (`str`):
User's full name.
"""

# Metadata
name: str
avatarUrl: str
fullName: str

def __init__(self, data: Dict) -> None:
self.name = data["name"]
self.avatarUrl = data["avatarUrl"]
self.fullName = data["fullName"]


def future_compatible(fn: CallableT) -> CallableT:
"""Wrap a method of `HfApi` to handle `run_as_future=True`.

Expand Down Expand Up @@ -1054,9 +1079,11 @@ def whoami(self, token: Optional[str] = None) -> Dict:
hf_raise_for_status(r)
except HTTPError as e:
raise HTTPError(
"Invalid user token. If you didn't pass a user token, make sure you "
"are properly logged in by executing `huggingface-cli login`, and "
"if you did pass a user token, double-check it's correct.",
(
"Invalid user token. If you didn't pass a user token, make sure you "
"are properly logged in by executing `huggingface-cli login`, and "
"if you did pass a user token, double-check it's correct."
),
request=e.request,
response=e.response,
) from e
Expand Down Expand Up @@ -1730,6 +1757,51 @@ 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 huggingface.co.
issamarabi marked this conversation as resolved.
Show resolved Hide resolved

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
path = f"{self.endpoint}/api/models/{repo_id}/likers"
if repo_type:
path = f"{self.endpoint}/api/{repo_type}/{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()
likers = [User(**user_data) for user_data in likers_data]

return likers
issamarabi marked this conversation as resolved.
Show resolved Hide resolved

@validate_hf_hub_args
def model_info(
self,
Expand Down Expand Up @@ -5612,9 +5684,11 @@ def request_space_hardware(
"""
if sleep_time is not None and hardware == SpaceHardware.CPU_BASIC:
warnings.warn(
"If your Space runs on the default 'cpu-basic' hardware, it will go to sleep if inactive for more"
" than 48 hours. This value is not configurable. If you don't want your Space to deactivate or if"
" you want to set a custom sleep time, you need to upgrade to a paid Hardware.",
(
"If your Space runs on the default 'cpu-basic' hardware, it will go to sleep if inactive for more"
" than 48 hours. This value is not configurable. If you don't want your Space to deactivate or if"
" you want to set a custom sleep time, you need to upgrade to a paid Hardware."
),
UserWarning,
)
payload: Dict[str, Any] = {"flavor": hardware}
Expand Down Expand Up @@ -5667,9 +5741,11 @@ def set_space_sleep_time(self, repo_id: str, sleep_time: int, *, token: Optional
hardware = runtime.requested_hardware or runtime.hardware
if hardware == SpaceHardware.CPU_BASIC:
warnings.warn(
"If your Space runs on the default 'cpu-basic' hardware, it will go to sleep if inactive for more"
" than 48 hours. This value is not configurable. If you don't want your Space to deactivate or if"
" you want to set a custom sleep time, you need to upgrade to a paid Hardware.",
(
"If your Space runs on the default 'cpu-basic' hardware, it will go to sleep if inactive for more"
" than 48 hours. This value is not configurable. If you don't want your Space to deactivate or if"
" you want to set a custom sleep time, you need to upgrade to a paid Hardware."
),
UserWarning,
)
return runtime
Expand Down Expand Up @@ -5845,9 +5921,11 @@ def duplicate_space(

if sleep_time is not None and hardware == SpaceHardware.CPU_BASIC:
warnings.warn(
"If your Space runs on the default 'cpu-basic' hardware, it will go to sleep if inactive for more"
" than 48 hours. This value is not configurable. If you don't want your Space to deactivate or if"
" you want to set a custom sleep time, you need to upgrade to a paid Hardware.",
(
"If your Space runs on the default 'cpu-basic' hardware, it will go to sleep if inactive for more"
" than 48 hours. This value is not configurable. If you don't want your Space to deactivate or if"
" you want to set a custom sleep time, you need to upgrade to a paid Hardware."
),
UserWarning,
)

Expand Down
22 changes: 20 additions & 2 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.name 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 Expand Up @@ -3134,8 +3150,10 @@ def test_repo_url_class(self):
# __repr__ is modified for debugging purposes
self.assertEqual(
repr(url),
"RepoUrl('https://huggingface.co/gpt2',"
" endpoint='https://huggingface.co', repo_type='model', repo_id='gpt2')",
(
"RepoUrl('https://huggingface.co/gpt2',"
" endpoint='https://huggingface.co', repo_type='model', repo_id='gpt2')"
),
)

def test_repo_url_endpoint(self):
Expand Down