Spaces:
Running on Zero
Running on Zero
File size: 6,298 Bytes
66ee87e 21c5089 66ee87e 21c5089 66ee87e 43c3b3c 21c5089 66ee87e abdbb98 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | """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)
|