Repository: huggingface/huggingface_hub
Pull request #1021 (2022-08-31 09:26:50)

Consider empty subfolder as None in `hf_hub_url` and `hf_hub_download`
Fix https://github.com/huggingface/huggingface_hub/issues/1016.

HuggingFaceDocBuilderDev: The docs for this PR live [here](https://moon-ci-docs.huggingface.co/docs/huggingface_hub/pr_1021). All of your documentation changes will be reflected on that endpoint.
codecov[bot]: # [Codecov](https://codecov.io/gh/huggingface/huggingface_hub/pull/1021?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface) Report
> Merging [#1021](https://codecov.io/gh/huggingface/huggingface_hub/pull/1021?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface) (792a81b) into [main](https://codecov.io/gh/huggingface/huggingface_hub/commit/48ddc62cbed37d2101b8dce2a34951c404629cd5?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface) (48ddc62) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##             main    #1021      +/-   ##
==========================================
+ Coverage   82.16%   82.18%   +0.01%     
==========================================
  Files          35       35              
  Lines        3588     3592       +4     
==========================================
+ Hits         2948     2952       +4     
  Misses        640      640              
```


| [Impacted Files](https://codecov.io/gh/huggingface/huggingface_hub/pull/1021?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface) | Coverage Δ | |
|---|---|---|
| [src/huggingface\_hub/file\_download.py](https://codecov.io/gh/huggingface/huggingface_hub/pull/1021/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface#diff-c3JjL2h1Z2dpbmdmYWNlX2h1Yi9maWxlX2Rvd25sb2FkLnB5) | `85.79% <100.00%> (+0.15%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface)


Patch:
diff --git a/src/huggingface_hub/file_download.py b/src/huggingface_hub/file_download.py
index 929c638e..e988cbd0 100644
--- a/src/huggingface_hub/file_download.py
+++ b/src/huggingface_hub/file_download.py
@@ -219,6 +219,8 @@ def hf_hub_url(
 
     -  [1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
     """
+    if subfolder == "":
+        subfolder = None
     if subfolder is not None:
         filename = f"{subfolder}/{filename}"
 
@@ -1032,6 +1034,8 @@ def hf_hub_download(
     if isinstance(cache_dir, Path):
         cache_dir = str(cache_dir)
 
+    if subfolder == "":
+        subfolder = None
     if subfolder is not None:
         # This is used to create a URL, and not a local path, hence the forward slash.
         filename = f"{subfolder}/{filename}"


Test patch:
diff --git a/tests/test_file_download.py b/tests/test_file_download.py
index 2abd0f7b..50b3d4ca 100644
--- a/tests/test_file_download.py
+++ b/tests/test_file_download.py
@@ -13,6 +13,7 @@
 # limitations under the License.
 import os
 import unittest
+from pathlib import Path
 from tempfile import TemporaryDirectory
 
 import pytest
@@ -247,6 +248,46 @@ class CachedDownloadTests(unittest.TestCase):
                 )
                 self.assertTrue(os.path.exists(filepath))
 
+    def test_hf_hub_download_with_empty_subfolder(self):
+        """
+        Check subfolder arg is processed correctly when empty string is passed to
+        `hf_hub_download`.
+
+        See https://github.com/huggingface/huggingface_hub/issues/1016.
+        """
+        filepath = Path(
+            hf_hub_download(
+                DUMMY_MODEL_ID,
+                filename=CONFIG_NAME,
+                subfolder="",  # Subfolder should be processed as `None`
+            )
+        )
+
+        # Check file exists and is not in a subfolder in cache
+        # e.g: "(...)/snapshots/<commit-id>/config.json"
+        self.assertTrue(filepath.is_file())
+        self.assertEqual(filepath.name, CONFIG_NAME)
+        self.assertEqual(Path(filepath).parent.parent.name, "snapshots")
+
+    def test_hf_hub_url_with_empty_subfolder(self):
+        """
+        Check subfolder arg is processed correctly when empty string is passed to
+        `hf_hub_url`.
+
+        See https://github.com/huggingface/huggingface_hub/issues/1016.
+        """
+        url = hf_hub_url(
+            DUMMY_MODEL_ID,
+            filename=CONFIG_NAME,
+            subfolder="",  # Subfolder should be processed as `None`
+        )
+        self.assertTrue(
+            url.endswith(
+                # "./resolve/main/config.json" and not "./resolve/main//config.json"
+                f"{DUMMY_MODEL_ID}/resolve/main/config.json",
+            )
+        )
+
     def test_hf_hub_download_legacy(self):
         filepath = hf_hub_download(
             DUMMY_MODEL_ID,