Skip to content

Commit

Permalink
Paginate followers and following endpoints (#2506)
Browse files Browse the repository at this point in the history
  • Loading branch information
Wauplin authored Sep 4, 2024
1 parent 36bc497 commit bec8b99
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 31 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ Follow these steps to start contributing:

7. Format your code.

`hugginface_hub` relies on [`ruff`](https://github.com/astral-sh/ruff) to format its source code consistently. You
`huggingface_hub` relies on [`ruff`](https://github.com/astral-sh/ruff) to format its source code consistently. You
can apply automatic style corrections and code verifications with the following command:

```bash
Expand Down
6 changes: 6 additions & 0 deletions src/huggingface_hub/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@
"list_liked_repos",
"list_metrics",
"list_models",
"list_organization_members",
"list_pending_access_requests",
"list_rejected_access_requests",
"list_repo_commits",
Expand All @@ -221,6 +222,8 @@
"list_repo_refs",
"list_repo_tree",
"list_spaces",
"list_user_followers",
"list_user_following",
"list_webhooks",
"merge_pull_request",
"model_info",
Expand Down Expand Up @@ -718,6 +721,7 @@ def __dir__():
list_liked_repos, # noqa: F401
list_metrics, # noqa: F401
list_models, # noqa: F401
list_organization_members, # noqa: F401
list_pending_access_requests, # noqa: F401
list_rejected_access_requests, # noqa: F401
list_repo_commits, # noqa: F401
Expand All @@ -726,6 +730,8 @@ def __dir__():
list_repo_refs, # noqa: F401
list_repo_tree, # noqa: F401
list_spaces, # noqa: F401
list_user_followers, # noqa: F401
list_user_following, # noqa: F401
list_webhooks, # noqa: F401
merge_pull_request, # noqa: F401
model_info, # noqa: F401
Expand Down
66 changes: 42 additions & 24 deletions src/huggingface_hub/hf_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9433,13 +9433,18 @@ def _validate_yaml(self, content: str, *, repo_type: Optional[str] = None, token
message = "\n".join([f"- {error.get('message')}" for error in errors])
raise ValueError(f"Invalid metadata in README.md.\n{message}") from e

def get_user_overview(self, username: str) -> User:
def get_user_overview(self, username: str, token: Union[bool, str, None] = None) -> User:
"""
Get an overview of a user on the Hub.
Args:
username (`str`):
Username of the user to get an overview of.
token (Union[bool, str, None], optional):
A valid user access token (string). Defaults to the locally saved
token, which is the recommended method for authentication (see
https://huggingface.co/docs/huggingface_hub/quick-start#authentication).
To disable authentication, pass `False`.
Returns:
`User`: A [`User`] object with the user's overview.
Expand All @@ -9448,18 +9453,24 @@ def get_user_overview(self, username: str) -> User:
[`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError):
HTTP 404 If the user does not exist on the Hub.
"""
r = get_session().get(f"{constants.ENDPOINT}/api/users/{username}/overview")

r = get_session().get(
f"{constants.ENDPOINT}/api/users/{username}/overview", headers=self._build_hf_headers(token=token)
)
hf_raise_for_status(r)
return User(**r.json())

def list_organization_members(self, organization: str) -> Iterable[User]:
def list_organization_members(self, organization: str, token: Union[bool, str, None] = None) -> Iterable[User]:
"""
List of members of an organization on the Hub.
Args:
organization (`str`):
Name of the organization to get the members of.
token (Union[bool, str, None], optional):
A valid user access token (string). Defaults to the locally saved
token, which is the recommended method for authentication (see
https://huggingface.co/docs/huggingface_hub/quick-start#authentication).
To disable authentication, pass `False`.
Returns:
`Iterable[User]`: A list of [`User`] objects with the members of the organization.
Expand All @@ -9469,21 +9480,25 @@ def list_organization_members(self, organization: str) -> Iterable[User]:
HTTP 404 If the organization does not exist on the Hub.
"""

r = get_session().get(f"{constants.ENDPOINT}/api/organizations/{organization}/members")

hf_raise_for_status(r)

for member in r.json():
for member in paginate(
path=f"{constants.ENDPOINT}/api/organizations/{organization}/members",
params={},
headers=self._build_hf_headers(token=token),
):
yield User(**member)

def list_user_followers(self, username: str) -> Iterable[User]:
def list_user_followers(self, username: str, token: Union[bool, str, None] = None) -> Iterable[User]:
"""
Get the list of followers of a user on the Hub.
Args:
username (`str`):
Username of the user to get the followers of.
token (Union[bool, str, None], optional):
A valid user access token (string). Defaults to the locally saved
token, which is the recommended method for authentication (see
https://huggingface.co/docs/huggingface_hub/quick-start#authentication).
To disable authentication, pass `False`.
Returns:
`Iterable[User]`: A list of [`User`] objects with the followers of the user.
Expand All @@ -9493,21 +9508,25 @@ def list_user_followers(self, username: str) -> Iterable[User]:
HTTP 404 If the user does not exist on the Hub.
"""

r = get_session().get(f"{constants.ENDPOINT}/api/users/{username}/followers")

hf_raise_for_status(r)

for follower in r.json():
for follower in paginate(
path=f"{constants.ENDPOINT}/api/users/{username}/followers",
params={},
headers=self._build_hf_headers(token=token),
):
yield User(**follower)

def list_user_following(self, username: str) -> Iterable[User]:
def list_user_following(self, username: str, token: Union[bool, str, None] = None) -> Iterable[User]:
"""
Get the list of users followed by a user on the Hub.
Args:
username (`str`):
Username of the user to get the users followed by.
token (Union[bool, str, None], optional):
A valid user access token (string). Defaults to the locally saved
token, which is the recommended method for authentication (see
https://huggingface.co/docs/huggingface_hub/quick-start#authentication).
To disable authentication, pass `False`.
Returns:
`Iterable[User]`: A list of [`User`] objects with the users followed by the user.
Expand All @@ -9517,12 +9536,11 @@ def list_user_following(self, username: str) -> Iterable[User]:
HTTP 404 If the user does not exist on the Hub.
"""

r = get_session().get(f"{constants.ENDPOINT}/api/users/{username}/following")

hf_raise_for_status(r)

for followed_user in r.json():
for followed_user in paginate(
path=f"{constants.ENDPOINT}/api/users/{username}/following",
params={},
headers=self._build_hf_headers(token=token),
):
yield User(**followed_user)


Expand Down
8 changes: 4 additions & 4 deletions tests/test_hf_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4086,12 +4086,12 @@ def test_organization_members(self) -> None:
assert len(list(members)) > 1

def test_user_followers(self) -> None:
followers = self.api.list_user_followers("julien-c")
assert len(list(followers)) > 10
followers = self.api.list_user_followers("clem")
assert len(list(followers)) > 500

def test_user_following(self) -> None:
following = self.api.list_user_following("julien-c")
assert len(list(following)) > 10
following = self.api.list_user_following("clem")
assert len(list(following)) > 500


class WebhookApiTest(HfApiCommonTest):
Expand Down
4 changes: 2 additions & 2 deletions utils/check_static_imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ def check_static_imports(update: bool) -> NoReturn:
"_SUBMOD_ATTRS = {\n"
+ "\n".join(
f' "{module}": [\n'
+ "\n".join(f' "{attr}",' for attr in sorted(_SUBMOD_ATTRS[module]))
+ "\n".join(f' "{attr}",' for attr in sorted(set(_SUBMOD_ATTRS[module])))
+ "\n ],"
for module in sorted(_SUBMOD_ATTRS.keys())
for module in sorted(set(_SUBMOD_ATTRS.keys()))
)
+ "\n}"
)
Expand Down

0 comments on commit bec8b99

Please sign in to comment.