Download make_space.py from DGring/bigears: direct link, hf CLI and curl.
- Browser
- Download file 5.93 kB
-
https://huggingface.co/spaces/DGring/bigears/resolve/main/make_space.py
- Command line
-
hf download hf://spaces/DGring/bigears/make_space.py
-
curl -L -o make_space.py https://huggingface.co/spaces/DGring/bigears/resolve/main/make_space.py
5.93 kB
| """Assemble the deployable Hugging Face Space directory. | |
| The Space is a *self-contained* git repository: Hugging Face clones it, runs | |
| ``pip install -r requirements.txt``, and starts ``app.py``. It has no access to | |
| this repo, so the ``soloscribe`` package has to travel with it. This script | |
| vendors it — a plain directory copy of ``soloscribe/`` into ``space/``, minus | |
| ``webapp/`` (the desktop FastAPI UI, which the Space replaces) and the usual | |
| build litter. | |
| .venv/bin/python space/make_space.py # copy the package in | |
| .venv/bin/python space/make_space.py --check # is the copy current? | |
| REDEPLOYING AFTER A CHANGE TO soloscribe/: rerun this script, then commit and | |
| push the Space repo. The vendored copy is a snapshot, not a symlink, so a | |
| change to ``soloscribe/transcribe.py`` reaches the Space only once this has run | |
| again. ``--check`` exits non-zero when the snapshot has drifted, which is the | |
| cheap way to find out before pushing rather than after. | |
| Hand-written files in ``space/`` (app.py, requirements.txt, packages.txt, | |
| README.md, this script) are never touched — the script only ever writes inside | |
| ``space/soloscribe/``. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import filecmp | |
| import shutil | |
| import sys | |
| from pathlib import Path | |
| SPACE_DIR = Path(__file__).resolve().parent | |
| REPO_ROOT = SPACE_DIR.parent | |
| SOURCE_PKG = REPO_ROOT / "soloscribe" | |
| VENDORED_PKG = SPACE_DIR / "soloscribe" | |
| # webapp/ is the desktop UI (FastAPI + static files); app.py is its replacement | |
| # and pulling it along would drag fastapi and uvicorn into the Space's | |
| # requirements for no reason. Nothing outside webapp/ imports it — verified by | |
| # grepping the package for "webapp". | |
| EXCLUDE_DIRS = {"webapp", "__pycache__", ".pytest_cache"} | |
| EXCLUDE_SUFFIXES = {".pyc", ".pyo"} | |
| # Everything the Space needs that this script does NOT generate. Listed so a | |
| # missing file is caught here rather than by a failed build on the platform. | |
| REQUIRED_FILES = ("app.py", "requirements.txt", "packages.txt", "README.md") | |
| def _wanted(root: Path) -> list[Path]: | |
| """Every file under `root` that belongs in the Space, as relative paths.""" | |
| out: list[Path] = [] | |
| for path in sorted(root.rglob("*")): | |
| rel = path.relative_to(root) | |
| if any(part in EXCLUDE_DIRS for part in rel.parts): | |
| continue | |
| if path.is_file() and path.suffix not in EXCLUDE_SUFFIXES: | |
| out.append(rel) | |
| return out | |
| def _copy(verbose: bool = True) -> list[Path]: | |
| if VENDORED_PKG.exists(): | |
| shutil.rmtree(VENDORED_PKG) | |
| files = _wanted(SOURCE_PKG) | |
| for rel in files: | |
| dest = VENDORED_PKG / rel | |
| dest.parent.mkdir(parents=True, exist_ok=True) | |
| shutil.copy2(SOURCE_PKG / rel, dest) | |
| if verbose: | |
| total = sum((VENDORED_PKG / r).stat().st_size for r in files) | |
| print(f"vendored {len(files)} files ({total / 1024:.0f} KiB) into {VENDORED_PKG}") | |
| for rel in files: | |
| print(f" soloscribe/{rel}") | |
| return files | |
| def _prune_litter() -> list[Path]: | |
| """Drop __pycache__ and friends from anywhere under space/. | |
| Running the app or the tests locally leaves bytecode caches inside the very | |
| directory that gets pushed, and `hf upload` sends what it is given. This | |
| runs on every pass so the directory is clean at the moment it is uploaded, | |
| rather than relying on whoever pushes to remember an --exclude flag. | |
| """ | |
| dropped: list[Path] = [] | |
| for path in sorted(SPACE_DIR.rglob("*"), reverse=True): | |
| if path.is_dir() and path.name in {"__pycache__", ".pytest_cache"}: | |
| shutil.rmtree(path) | |
| dropped.append(path.relative_to(SPACE_DIR)) | |
| return dropped | |
| def _check() -> int: | |
| """Exit 0 when the vendored copy matches the package it was made from.""" | |
| if not VENDORED_PKG.exists(): | |
| print(f"MISSING: {VENDORED_PKG} — run this script without --check", file=sys.stderr) | |
| return 1 | |
| want = set(_wanted(SOURCE_PKG)) | |
| have = set(_wanted(VENDORED_PKG)) | |
| problems: list[str] = [] | |
| for rel in sorted(want - have): | |
| problems.append(f"missing from the Space: soloscribe/{rel}") | |
| for rel in sorted(have - want): | |
| problems.append(f"stale, not in the source: soloscribe/{rel}") | |
| for rel in sorted(want & have): | |
| if not filecmp.cmp(SOURCE_PKG / rel, VENDORED_PKG / rel, shallow=False): | |
| problems.append(f"out of date: soloscribe/{rel}") | |
| if problems: | |
| print("The vendored package has drifted from soloscribe/:", file=sys.stderr) | |
| for line in problems: | |
| print(f" {line}", file=sys.stderr) | |
| print("\nRerun: .venv/bin/python space/make_space.py", file=sys.stderr) | |
| return 1 | |
| print(f"vendored copy is current ({len(want)} files)") | |
| return 0 | |
| def main(argv: list[str] | None = None) -> int: | |
| ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) | |
| ap.add_argument( | |
| "--check", | |
| action="store_true", | |
| help="verify the vendored copy is current; do not write anything", | |
| ) | |
| ap.add_argument("--quiet", action="store_true", help="print only the summary line") | |
| args = ap.parse_args(argv) | |
| if not SOURCE_PKG.is_dir(): | |
| print(f"cannot find the package at {SOURCE_PKG}", file=sys.stderr) | |
| return 2 | |
| missing = [f for f in REQUIRED_FILES if not (SPACE_DIR / f).is_file()] | |
| if missing: | |
| print( | |
| "these hand-written Space files are missing: " + ", ".join(missing), | |
| file=sys.stderr, | |
| ) | |
| return 2 | |
| if args.check: | |
| return _check() | |
| _copy(verbose=not args.quiet) | |
| for rel in _prune_litter(): | |
| if not args.quiet: | |
| print(f" removed {rel}") | |
| print(f"\nSpace directory ready: {SPACE_DIR}") | |
| print("Push this directory to the Space repo — see space/README.md.") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |