diff --git a/src/huggingface_hub/hf_api.py b/src/huggingface_hub/hf_api.py index a7b30415ae..385728d8cf 100644 --- a/src/huggingface_hub/hf_api.py +++ b/src/huggingface_hub/hf_api.py @@ -398,6 +398,7 @@ def upload_file( repo_id: str, repo_type: Optional[str] = None, revision: Optional[str] = None, + identical_ok: bool = True, ) -> str: """ Upload a local file (up to 5GB) to the given repo, tracking it with LFS if it's larger than 10MB @@ -421,6 +422,10 @@ def upload_file( revision (``str``, Optional): The git revision to commit from. Defaults to the :obj:`"main"` branch. + identical_ok (``bool``, defaults to ``True``): + When set to false, will raise an HTTPError when the file you're trying to upload already exists on the hub + and its content did not change. + Returns: ``str``: The URL to visualize the uploaded file on the hub @@ -501,7 +506,12 @@ def upload_file( else: r = requests.post(path, headers=headers, data=path_or_fileobj) - r.raise_for_status() + try: + r.raise_for_status() + except HTTPError as err: + if not (identical_ok and err.response.status_code == 409): + raise err + d = r.json() return d["url"] diff --git a/tests/test_hf_api.py b/tests/test_hf_api.py index 3972a832c6..cb5c36032c 100644 --- a/tests/test_hf_api.py +++ b/tests/test_hf_api.py @@ -237,6 +237,42 @@ def test_upload_file_bytesio(self): finally: self._api.delete_repo(token=self._token, name=REPO_NAME) + def test_upload_file_conflict(self): + self._api.create_repo(token=self._token, name=REPO_NAME) + try: + filecontent = BytesIO(b"File content, but in bytes IO") + self._api.upload_file( + path_or_fileobj=filecontent, + path_in_repo="temp/new_file.md", + repo_id=f"{USER}/{REPO_NAME}", + token=self._token, + identical_ok=True, + ) + + # No exception raised when identical_ok is True + self._api.upload_file( + path_or_fileobj=filecontent, + path_in_repo="temp/new_file.md", + repo_id=f"{USER}/{REPO_NAME}", + token=self._token, + identical_ok=True, + ) + + with self.assertRaises(HTTPError) as err_ctx: + self._api.upload_file( + path_or_fileobj=filecontent, + path_in_repo="temp/new_file.md", + repo_id=f"{USER}/{REPO_NAME}", + token=self._token, + identical_ok=False, + ) + self.assertEqual(err_ctx.exception.response.status_code, 409) + + except Exception as err: + self.fail(err) + finally: + self._api.delete_repo(token=self._token, name=REPO_NAME) + class HfApiPublicTest(unittest.TestCase): def test_staging_list_models(self):