Repository: huggingface/huggingface_hub
Pull request #1285 (2023-01-04 22:43:46)

Update `REGEX_YAML_BLOCK`
Update `REGEX_YAML_BLOCK` to prevent matching metadata section in modelcards if some content is placed before it. Avoid having a false positive in case of a code section inside the modelcard.

HuggingFaceDocBuilderDev: The docs for this PR live [here](https://moon-ci-docs.huggingface.co/docs/huggingface_hub/pr_1285). All of your documentation changes will be reflected on that endpoint.
julien-c: yes @nateraw correct, but maybe let's keep one with whitespace above the first --- as it should match in that case?
Wauplin: I've made the change suggested by @nateraw in https://github.com/huggingface/huggingface_hub/pull/1285/commits/2564e8c8f6862825493cb0adafb7c193ce51ba4d and added simple tests for the regex in https://github.com/huggingface/huggingface_hub/pull/1285/commits/950a20fb30bd17944666e6a16cd4150de43dbf77 (and also updated the PR description).

@julien-c @nateraw could you quickly review before I merge ?

Patch:
diff --git a/src/huggingface_hub/repocard.py b/src/huggingface_hub/repocard.py
index efb87587..84cddcba 100644
--- a/src/huggingface_hub/repocard.py
+++ b/src/huggingface_hub/repocard.py
@@ -29,7 +29,7 @@ TEMPLATE_DATASETCARD_PATH = (
 )
 
 # exact same regex as in the Hub server. Please keep in sync.
-REGEX_YAML_BLOCK = re.compile(r"---[\n\r]+([\S\s]*?)[\n\r]+---[\n\r]")
+REGEX_YAML_BLOCK = re.compile(r"^(\s*---[\n\r]+)([\S\s]*?)([\n\r]+---([\n\r]|$))")
 
 logger = get_logger(__name__)
 
@@ -91,7 +91,7 @@ class RepoCard:
         match = REGEX_YAML_BLOCK.search(content)
         if match:
             # Metadata found in the YAML block
-            yaml_block = match.group(1)
+            yaml_block = match.group(2)
             self.text = content[match.end() :]
             data_dict = yaml.safe_load(yaml_block)
 
@@ -494,7 +494,7 @@ def metadata_load(local_path: Union[str, Path]) -> Optional[Dict]:
     content = Path(local_path).read_text()
     match = REGEX_YAML_BLOCK.search(content)
     if match:
-        yaml_block = match.group(1)
+        yaml_block = match.group(2)
         data = yaml.safe_load(yaml_block)
         if isinstance(data, dict):
             return data


Test patch:
diff --git a/tests/test_repocard.py b/tests/test_repocard.py
index dbe41718..9a0b8303 100644
--- a/tests/test_repocard.py
+++ b/tests/test_repocard.py
@@ -38,7 +38,7 @@ from huggingface_hub import (
 from huggingface_hub.constants import REPOCARD_NAME
 from huggingface_hub.file_download import hf_hub_download
 from huggingface_hub.hf_api import HfApi
-from huggingface_hub.repocard import RepoCard
+from huggingface_hub.repocard import REGEX_YAML_BLOCK, RepoCard
 from huggingface_hub.repocard_data import CardData
 from huggingface_hub.repository import Repository
 from huggingface_hub.utils import SoftTemporaryDirectory, is_jinja_available, logging
@@ -71,9 +71,6 @@ widget:
 """
 
 DUMMY_MODELCARD = """
-
-Hi
-
 ---
 license: mit
 datasets:
@@ -84,11 +81,7 @@ datasets:
 Hello
 """
 
-DUMMY_MODELCARD_TARGET = """
-
-Hi
-
----
+DUMMY_MODELCARD_TARGET = """---
 meaning_of_life: 42
 ---
 
@@ -766,6 +759,17 @@ class RepoCardTest(TestCaseWithCapLog):
         )
 
 
+class TestRegexYamlBlock(unittest.TestCase):
+    def test_match_with_leading_whitespace(self):
+        self.assertIsNotNone(REGEX_YAML_BLOCK.search("   \n---\nmetadata: 1\n---"))
+
+    def test_match_without_leading_whitespace(self):
+        self.assertIsNotNone(REGEX_YAML_BLOCK.search("---\nmetadata: 1\n---"))
+
+    def test_does_not_match_with_leading_text(self):
+        self.assertIsNone(REGEX_YAML_BLOCK.search("something\n---\nmetadata: 1\n---"))
+
+
 class ModelCardTest(TestCaseWithCapLog):
     def test_model_card_with_invalid_model_index(self):
         """