diff --git a/docs/source/en/guides/repository.md b/docs/source/en/guides/repository.md index 4e6aab5b8d..77bf797e61 100644 --- a/docs/source/en/guides/repository.md +++ b/docs/source/en/guides/repository.md @@ -155,6 +155,19 @@ A repository can be public or private. A private repository is only visible to y >>> update_repo_visibility(repo_id=repo_id, private=True) ``` +### Setup gated access + +To give more control over how repos are used, the Hub allows repo authors to enable **access requests** for their repos. User must agree to share their contact information (username and email address) with the repo authors to access the files when enabled. A repo with access requests enabled is called a **gated repo**. + +You can set a repo as gated using [`update_repo_settings`]: + +```py +>>> from huggingface_hub import HfApi + +>>> api = HfApi() +>>> api.update_repo_settings(repo_id=repo_id, gated="auto") # Set automatic gating for a model +``` + ### Rename your repository You can rename your repository on the Hub using [`move_repo`]. Using this method, you can also move the repo from a user to diff --git a/src/huggingface_hub/__init__.py b/src/huggingface_hub/__init__.py index 55aa356899..413fac30fd 100644 --- a/src/huggingface_hub/__init__.py +++ b/src/huggingface_hub/__init__.py @@ -252,6 +252,7 @@ "update_collection_item", "update_collection_metadata", "update_inference_endpoint", + "update_repo_settings", "update_repo_visibility", "update_webhook", "upload_file", @@ -761,6 +762,7 @@ def __dir__(): update_collection_item, # noqa: F401 update_collection_metadata, # noqa: F401 update_inference_endpoint, # noqa: F401 + update_repo_settings, # noqa: F401 update_repo_visibility, # noqa: F401 update_webhook, # noqa: F401 upload_file, # noqa: F401 diff --git a/src/huggingface_hub/hf_api.py b/src/huggingface_hub/hf_api.py index aeb77ea246..50643fa626 100644 --- a/src/huggingface_hub/hf_api.py +++ b/src/huggingface_hub/hf_api.py @@ -3564,6 +3564,62 @@ def update_repo_visibility( hf_raise_for_status(r) return r.json() + @validate_hf_hub_args + def update_repo_settings( + self, + repo_id: str, + *, + gated: Literal["auto", "manual", False] = False, + token: Union[str, bool, None] = None, + repo_type: Optional[str] = None, + ) -> None: + """ + Update the gated settings of a repository. + To give more control over how repos are used, the Hub allows repo authors to enable **access requests** for their repos. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated by a /. + gated (`Literal["auto", "manual", False]`, *optional*): + The gated release status for the repository. + * "auto": The repository is gated, and access requests are automatically approved or denied based on predefined criteria. + * "manual": The repository is gated, and access requests require manual approval. + * False (default): The repository is not gated, and anyone can access it. + token (`Union[str, bool, 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. + repo_type (`str`, *optional*): + The type of the repository to update settings from (`"model"`, `"dataset"` or `"space"`. + Defaults to `"model"`. + + Raises: + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If gated is not one of "auto", "manual", or False. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If repo_type is not one of the values in constants.REPO_TYPES. + [`~utils.HfHubHTTPError`]: + If the request to the Hugging Face Hub API fails. + """ + if gated not in ["auto", "manual", False]: + raise ValueError(f"Invalid gated status, must be one of 'auto', 'manual', or False. Got '{gated}'.") + + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type, must be one of {constants.REPO_TYPES}") + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL # default repo type + + # Build headers + headers = self._build_hf_headers(token=token) + + r = get_session().put( + url=f"{self.endpoint}/api/{repo_type}s/{repo_id}/settings", + headers=headers, + json={"gated": gated}, + ) + hf_raise_for_status(r) + def move_repo( self, from_id: str, @@ -9678,6 +9734,7 @@ def _parse_revision_from_pr_url(pr_url: str) -> str: create_repo = api.create_repo delete_repo = api.delete_repo update_repo_visibility = api.update_repo_visibility +update_repo_settings = api.update_repo_settings super_squash_history = api.super_squash_history move_repo = api.move_repo upload_file = api.upload_file diff --git a/tests/test_hf_api.py b/tests/test_hf_api.py index c828038bae..44e228871f 100644 --- a/tests/test_hf_api.py +++ b/tests/test_hf_api.py @@ -281,6 +281,25 @@ def test_move_repo_invalid_repo_id(self) -> None: with pytest.raises(ValueError, match=r"Invalid repo_id*"): self._api.move_repo(from_id="invalid_repo_id", to_id="namespace/repo_name") + @use_tmp_repo(repo_type="model") + def test_update_repo_settings(self, repo_url: RepoUrl): + repo_id = repo_url.repo_id + + for gated_value in ["auto", "manual", False]: + self._api.update_repo_settings(repo_id=repo_id, gated=gated_value) + info = self._api.model_info(repo_id, expand="gated") + assert info.gated == gated_value + + @use_tmp_repo(repo_type="dataset") + def test_update_dataset_repo_settings(self, repo_url: RepoUrl): + repo_id = repo_url.repo_id + repo_type = repo_url.repo_type + + for gated_value in ["auto", "manual", False]: + self._api.update_repo_settings(repo_id=repo_id, repo_type=repo_type, gated=gated_value) + info = self._api.dataset_info(repo_id, expand="gated") + assert info.gated == gated_value + class CommitApiTest(HfApiCommonTest): def setUp(self) -> None: