"""The Hugging Face Space packaging: front matter, requirements, entry point and LFS rules.""" import re import unittest from pathlib import Path ROOT = Path(__file__).resolve().parent.parent def front_matter(): text = (ROOT / "README.md").read_text(encoding="utf-8") m = re.match(r"---\n(.*?)\n---\n", text, re.S) if not m: raise AssertionError("README.md must start with YAML front matter for the Space") out = {} for line in m.group(1).splitlines(): k, _, v = line.partition(":") out[k.strip()] = v.strip().strip('"') return out def pins(path): out = {} for line in (ROOT / path).read_text().splitlines(): line = line.split("#", 1)[0].strip() if line and not line.startswith("-"): name, _, version = line.partition("==") out[re.sub(r"\[.*\]", "", name).lower().replace("_", "-")] = version return out @unittest.skipUnless((ROOT / "Dockerfile").is_file(), "packaging files are not in the image") class SpacePackagingTest(unittest.TestCase): def test_front_matter_declares_a_gradio_space(self): fm = front_matter() self.assertEqual((fm["sdk"], fm["app_file"]), ("gradio", "app.py")) # Hugging Face's default name self.assertIn(fm["python_version"], ("3.10", "3.12")) # the Pythons ZeroGPU supports self.assertTrue((ROOT / fm["app_file"]).is_file()) self.assertEqual(fm["title"], "DecisionLab") def test_sdk_version_matches_the_gradio_pin(self): self.assertEqual(front_matter()["sdk_version"], pins("requirements.txt")["gradio"]) def test_model_stack_is_pinned_exactly(self): req = pins("requirements.txt") for name in ("torch", "transformers", "safetensors", "tokenizers", "huggingface-hub", "laya", "numpy", "gradio"): with self.subTest(package=name): self.assertRegex(req.get(name, ""), r"^\d+\.\d+") def test_torch_is_a_zerogpu_supported_cuda_build(self): """ZeroGPU's configuration check (2026-09-28): 2.13.0, 2.12.1, 2.11.0, 2.10.0, 2.9.1, 2.8.0.""" self.assertIn(pins("requirements.txt")["torch"], ("2.13.0", "2.12.1", "2.11.0", "2.10.0", "2.9.1", "2.8.0")) self.assertNotIn("download.pytorch.org/whl/cpu", (ROOT / "requirements.txt").read_text()) def test_spaces_package_is_required(self): self.assertIn("spaces", pins("requirements.txt")) def test_spaces_is_imported_before_anything_else(self): import ast tree = ast.parse((ROOT / "app.py").read_text(encoding="utf-8")) imports = [n for n in tree.body if isinstance(n, (ast.Import, ast.ImportFrom)) and not (isinstance(n, ast.ImportFrom) and n.module == "__future__")] first = imports[0] self.assertEqual(first.names[0].name if isinstance(first, ast.Import) else first.module, "spaces") def test_web_stack_is_left_to_gradio(self): req = pins("requirements.txt") for name in ("fastapi", "starlette", "pydantic", "uvicorn"): with self.subTest(package=name): self.assertNotIn(name, req) def test_model_versions_match_the_container(self): space, container = pins("requirements.txt"), pins("constraints.txt") for name in ("transformers", "safetensors", "tokenizers", "huggingface-hub", "laya", "numpy"): with self.subTest(package=name): self.assertEqual(space[name], container[name]) def test_weights_go_through_git_lfs(self): attrs = (ROOT / ".gitattributes").read_text() for pattern in ("*.safetensors", "tokenizer.json"): with self.subTest(pattern=pattern): self.assertRegex(attrs, rf"(?m)^{re.escape(pattern)} filter=lfs diff=lfs merge=lfs -text$") def test_the_space_starts_through_gradio_launch(self): """ZeroGPU's startup step runs inside Gradio's Blocks.launch(); uvicorn + mount_gradio_app skips it ("No @spaces.GPU function detected during startup", 2026-09-30).""" import ast tree = ast.parse((ROOT / "app.py").read_text(encoding="utf-8")) calls = {n.func.attr for n in ast.walk(tree) if isinstance(n, ast.Call) and isinstance(n.func, ast.Attribute)} imported = {a.name for n in ast.walk(tree) if isinstance(n, ast.Import) for a in n.names} self.assertIn("launch", calls) self.assertNotIn("mount_gradio_app", calls) self.assertNotIn("uvicorn", imported) def test_no_leftover_space_py(self): self.assertFalse((ROOT / "space.py").exists()) def test_app_py_imports_the_app_package_not_itself(self): import subprocess, sys r = subprocess.run([sys.executable, "-c", "import sys; sys.path.insert(0, '.'); import app.validation; print(app.validation.__file__)"], cwd=ROOT, capture_output=True, text=True) self.assertEqual(r.returncode, 0, r.stderr) self.assertIn("app", r.stdout) def test_models_folder_exists_for_local_models(self): self.assertTrue((ROOT / "models").is_dir()) if __name__ == "__main__": unittest.main() @unittest.skipUnless((ROOT / "Dockerfile").is_file(), "packaging files are not in the image") class ModelsFolderReachesTheSpaceTest(unittest.TestCase): """A Gradio Space is built with Docker from this repo, so .dockerignore applies to it. Excluding models/ there kept the Space's local models out of its image (2.2.0, 2026-09-30).""" def ignored(self): return [l.strip() for l in (ROOT / ".dockerignore").read_text().splitlines() if l.strip() and not l.startswith("#")] def test_dockerignore_does_not_exclude_the_models_folder(self): for pattern in self.ignored(): with self.subTest(pattern=pattern): self.assertFalse(pattern.rstrip("/").lstrip("./") in ("models", "models/*", "models/**")) def test_the_container_image_still_leaves_models_out(self): copies = [l for l in (ROOT / "Dockerfile").read_text().splitlines() if l.startswith(("COPY", "ADD"))] for line in copies: with self.subTest(line=line): self.assertNotRegex(line, r"^(COPY|ADD)\s+(--\S+\s+)*\.\s") # no "COPY . ..." self.assertNotIn("models", line)