Skip to content

Commit bec8b99

Browse files
authored
Paginate followers and following endpoints (#2506)
1 parent 36bc497 commit bec8b99

File tree

5 files changed

+55
-31
lines changed

5 files changed

+55
-31
lines changed

CONTRIBUTING.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ Follow these steps to start contributing:
148148

149149
7. Format your code.
150150

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

154154
```bash

src/huggingface_hub/__init__.py

+6
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@
213213
"list_liked_repos",
214214
"list_metrics",
215215
"list_models",
216+
"list_organization_members",
216217
"list_pending_access_requests",
217218
"list_rejected_access_requests",
218219
"list_repo_commits",
@@ -221,6 +222,8 @@
221222
"list_repo_refs",
222223
"list_repo_tree",
223224
"list_spaces",
225+
"list_user_followers",
226+
"list_user_following",
224227
"list_webhooks",
225228
"merge_pull_request",
226229
"model_info",
@@ -718,6 +721,7 @@ def __dir__():
718721
list_liked_repos, # noqa: F401
719722
list_metrics, # noqa: F401
720723
list_models, # noqa: F401
724+
list_organization_members, # noqa: F401
721725
list_pending_access_requests, # noqa: F401
722726
list_rejected_access_requests, # noqa: F401
723727
list_repo_commits, # noqa: F401
@@ -726,6 +730,8 @@ def __dir__():
726730
list_repo_refs, # noqa: F401
727731
list_repo_tree, # noqa: F401
728732
list_spaces, # noqa: F401
733+
list_user_followers, # noqa: F401
734+
list_user_following, # noqa: F401
729735
list_webhooks, # noqa: F401
730736
merge_pull_request, # noqa: F401
731737
model_info, # noqa: F401

src/huggingface_hub/hf_api.py

+42-24
Original file line numberDiff line numberDiff line change
@@ -9433,13 +9433,18 @@ def _validate_yaml(self, content: str, *, repo_type: Optional[str] = None, token
94339433
message = "\n".join([f"- {error.get('message')}" for error in errors])
94349434
raise ValueError(f"Invalid metadata in README.md.\n{message}") from e
94359435

9436-
def get_user_overview(self, username: str) -> User:
9436+
def get_user_overview(self, username: str, token: Union[bool, str, None] = None) -> User:
94379437
"""
94389438
Get an overview of a user on the Hub.
94399439
94409440
Args:
94419441
username (`str`):
94429442
Username of the user to get an overview of.
9443+
token (Union[bool, str, None], optional):
9444+
A valid user access token (string). Defaults to the locally saved
9445+
token, which is the recommended method for authentication (see
9446+
https://huggingface.co/docs/huggingface_hub/quick-start#authentication).
9447+
To disable authentication, pass `False`.
94439448
94449449
Returns:
94459450
`User`: A [`User`] object with the user's overview.
@@ -9448,18 +9453,24 @@ def get_user_overview(self, username: str) -> User:
94489453
[`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError):
94499454
HTTP 404 If the user does not exist on the Hub.
94509455
"""
9451-
r = get_session().get(f"{constants.ENDPOINT}/api/users/{username}/overview")
9452-
9456+
r = get_session().get(
9457+
f"{constants.ENDPOINT}/api/users/{username}/overview", headers=self._build_hf_headers(token=token)
9458+
)
94539459
hf_raise_for_status(r)
94549460
return User(**r.json())
94559461

9456-
def list_organization_members(self, organization: str) -> Iterable[User]:
9462+
def list_organization_members(self, organization: str, token: Union[bool, str, None] = None) -> Iterable[User]:
94579463
"""
94589464
List of members of an organization on the Hub.
94599465
94609466
Args:
94619467
organization (`str`):
94629468
Name of the organization to get the members of.
9469+
token (Union[bool, str, None], optional):
9470+
A valid user access token (string). Defaults to the locally saved
9471+
token, which is the recommended method for authentication (see
9472+
https://huggingface.co/docs/huggingface_hub/quick-start#authentication).
9473+
To disable authentication, pass `False`.
94639474
94649475
Returns:
94659476
`Iterable[User]`: A list of [`User`] objects with the members of the organization.
@@ -9469,21 +9480,25 @@ def list_organization_members(self, organization: str) -> Iterable[User]:
94699480
HTTP 404 If the organization does not exist on the Hub.
94709481
94719482
"""
9472-
9473-
r = get_session().get(f"{constants.ENDPOINT}/api/organizations/{organization}/members")
9474-
9475-
hf_raise_for_status(r)
9476-
9477-
for member in r.json():
9483+
for member in paginate(
9484+
path=f"{constants.ENDPOINT}/api/organizations/{organization}/members",
9485+
params={},
9486+
headers=self._build_hf_headers(token=token),
9487+
):
94789488
yield User(**member)
94799489

9480-
def list_user_followers(self, username: str) -> Iterable[User]:
9490+
def list_user_followers(self, username: str, token: Union[bool, str, None] = None) -> Iterable[User]:
94819491
"""
94829492
Get the list of followers of a user on the Hub.
94839493
94849494
Args:
94859495
username (`str`):
94869496
Username of the user to get the followers of.
9497+
token (Union[bool, str, None], optional):
9498+
A valid user access token (string). Defaults to the locally saved
9499+
token, which is the recommended method for authentication (see
9500+
https://huggingface.co/docs/huggingface_hub/quick-start#authentication).
9501+
To disable authentication, pass `False`.
94879502
94889503
Returns:
94899504
`Iterable[User]`: A list of [`User`] objects with the followers of the user.
@@ -9493,21 +9508,25 @@ def list_user_followers(self, username: str) -> Iterable[User]:
94939508
HTTP 404 If the user does not exist on the Hub.
94949509
94959510
"""
9496-
9497-
r = get_session().get(f"{constants.ENDPOINT}/api/users/{username}/followers")
9498-
9499-
hf_raise_for_status(r)
9500-
9501-
for follower in r.json():
9511+
for follower in paginate(
9512+
path=f"{constants.ENDPOINT}/api/users/{username}/followers",
9513+
params={},
9514+
headers=self._build_hf_headers(token=token),
9515+
):
95029516
yield User(**follower)
95039517

9504-
def list_user_following(self, username: str) -> Iterable[User]:
9518+
def list_user_following(self, username: str, token: Union[bool, str, None] = None) -> Iterable[User]:
95059519
"""
95069520
Get the list of users followed by a user on the Hub.
95079521
95089522
Args:
95099523
username (`str`):
95109524
Username of the user to get the users followed by.
9525+
token (Union[bool, str, None], optional):
9526+
A valid user access token (string). Defaults to the locally saved
9527+
token, which is the recommended method for authentication (see
9528+
https://huggingface.co/docs/huggingface_hub/quick-start#authentication).
9529+
To disable authentication, pass `False`.
95119530
95129531
Returns:
95139532
`Iterable[User]`: A list of [`User`] objects with the users followed by the user.
@@ -9517,12 +9536,11 @@ def list_user_following(self, username: str) -> Iterable[User]:
95179536
HTTP 404 If the user does not exist on the Hub.
95189537
95199538
"""
9520-
9521-
r = get_session().get(f"{constants.ENDPOINT}/api/users/{username}/following")
9522-
9523-
hf_raise_for_status(r)
9524-
9525-
for followed_user in r.json():
9539+
for followed_user in paginate(
9540+
path=f"{constants.ENDPOINT}/api/users/{username}/following",
9541+
params={},
9542+
headers=self._build_hf_headers(token=token),
9543+
):
95269544
yield User(**followed_user)
95279545

95289546

tests/test_hf_api.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -4086,12 +4086,12 @@ def test_organization_members(self) -> None:
40864086
assert len(list(members)) > 1
40874087

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

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

40964096

40974097
class WebhookApiTest(HfApiCommonTest):

utils/check_static_imports.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ def check_static_imports(update: bool) -> NoReturn:
5656
"_SUBMOD_ATTRS = {\n"
5757
+ "\n".join(
5858
f' "{module}": [\n'
59-
+ "\n".join(f' "{attr}",' for attr in sorted(_SUBMOD_ATTRS[module]))
59+
+ "\n".join(f' "{attr}",' for attr in sorted(set(_SUBMOD_ATTRS[module])))
6060
+ "\n ],"
61-
for module in sorted(_SUBMOD_ATTRS.keys())
61+
for module in sorted(set(_SUBMOD_ATTRS.keys()))
6262
)
6363
+ "\n}"
6464
)

0 commit comments

Comments
 (0)