ahmaddarkhalil Claude Opus 4.8 commited on
Commit
1bb28ed
Β·
1 Parent(s): 23149dd

Fix HfFolder shim ordering + cache mmcv wheel on the Hub

Browse files

- spaces itself imports HfFolder, and we moved `import spaces` to the top, so
the shim must run before it. Move the HfFolder shim above `import spaces`
(it only needs huggingface_hub, no CUDA).
- Cache the compiled mmcv-full wheel to the Hub (keyed by torch/cuda/python)
and reuse it on later cold starts, so the ~9 min compile happens only once.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Files changed (1) hide show
  1. app.py +92 -55
app.py CHANGED
@@ -9,6 +9,32 @@ import subprocess
9
  import tempfile
10
  import traceback
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  # ZeroGPU: `spaces` MUST be imported before torch / any CUDA-related package,
13
  # otherwise it raises "CUDA has been initialized before importing the spaces
14
  # package". The mmcv bootstrap below imports torch, so import spaces first.
@@ -26,48 +52,87 @@ else:
26
  sys.path.insert(0, REPO)
27
  sys.path.insert(0, os.path.join(REPO, "demo")) # for `import configs` inside helpers.py
28
 
29
- # ── ensure mmcv-full (with CUDA ops) is importable; build at runtime if not ──
30
- # HF's build phase can't compile mmcv (isolated pip, no Space Variables). We do
31
- # it here, where Variables are present and we control pip flags. Diagnostics are
32
- # printed so the container "run" logs reveal the toolchain state.
 
 
 
 
 
33
  def _ensure_mmcv():
34
  import shutil
 
 
35
  def run(cmd, env=None, check=True):
36
  print("[bootstrap] $", " ".join(cmd), flush=True)
37
  return subprocess.run(cmd, env=env, check=check)
38
 
39
- try:
40
- import mmcv
41
- from mmcv.ops import RoIAlign # noqa: F401 β€” proves CUDA ops compiled
42
- print(f"[bootstrap] mmcv {mmcv.__version__} (with ops) already present",
43
- flush=True)
 
 
 
 
 
 
44
  return
45
- except Exception as e:
46
- print(f"[bootstrap] mmcv/ops unavailable ({e!r}); building from source",
47
- flush=True)
48
 
49
  import torch
50
- print(f"[bootstrap] torch={torch.__version__} "
51
- f"torch.version.cuda={torch.version.cuda}", flush=True)
52
- nvcc = shutil.which("nvcc")
53
- print(f"[bootstrap] system nvcc: {nvcc}", flush=True)
54
- run(["bash", "-lc", "nvcc --version || true"], check=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  run(["bash", "-lc", "gcc --version | head -1 || true"], check=False)
56
  print(f"[bootstrap] CUDA_HOME={os.environ.get('CUDA_HOME')}", flush=True)
57
-
58
  env = dict(os.environ)
59
  env["MMCV_WITH_OPS"] = "1"
60
  env["FORCE_CUDA"] = "1"
61
- # RTX Pro 6000 Blackwell == sm_120; +PTX for forward-compat JIT.
62
- env.setdefault("TORCH_CUDA_ARCH_LIST", "12.0+PTX")
63
  env.setdefault("MAX_JOBS", "4")
64
- # Bare git URL (no "name @"): mmcv builds as "mmcv-full" with ops, and the
65
- # name@ form makes pip reject it for an inconsistent project name.
66
- run([sys.executable, "-m", "pip", "install", "--no-build-isolation", "-v",
67
- "git+https://github.com/open-mmlab/mmcv.git@v1.7.2"], env=env)
68
-
69
- import mmcv # noqa: F811
70
- print(f"[bootstrap] built mmcv {mmcv.__version__}", flush=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
 
73
  _ensure_mmcv()
@@ -84,34 +149,6 @@ del _mmcv_mod, _real_mmcv_ver
84
 
85
  import mmcv
86
  import numpy as np
87
-
88
- # ── HfFolder shim ─────────────────────────────────────────────────────────────
89
- # gradio 4.44 (pinned by the Space sdk_version, with the [oauth] extra forced by
90
- # HF) does `from huggingface_hub import HfFolder`, but the container ships
91
- # huggingface_hub 1.x which removed HfFolder. Restore a minimal stand-in so the
92
- # gradio.oauth import succeeds. We don't use OAuth, so behaviour is irrelevant.
93
- import huggingface_hub as _hfh
94
- if not hasattr(_hfh, "HfFolder"):
95
- class _HfFolderShim:
96
- path_token = None
97
-
98
- @staticmethod
99
- def get_token():
100
- return (os.environ.get("HF_TOKEN")
101
- or os.environ.get("HUGGING_FACE_HUB_TOKEN"))
102
-
103
- @classmethod
104
- def save_token(cls, token):
105
- pass
106
-
107
- @classmethod
108
- def delete_token(cls):
109
- pass
110
-
111
- _hfh.HfFolder = _HfFolderShim
112
- del _hfh
113
- # ─────────────────────────────────────────────────────────────────────────────
114
-
115
  import gradio as gr
116
  from huggingface_hub import hf_hub_download
117
 
 
9
  import tempfile
10
  import traceback
11
 
12
+ # ── HfFolder shim (must precede `import spaces` and `import gradio`) ──────────
13
+ # gradio 4.44's oauth.py AND the `spaces` package both do
14
+ # `from huggingface_hub import HfFolder`, but the container ships
15
+ # huggingface_hub 1.x which removed HfFolder. Inject a minimal stand-in. This
16
+ # only imports huggingface_hub (no CUDA), so it is safe to run before `spaces`.
17
+ import huggingface_hub as _hfh
18
+ if not hasattr(_hfh, "HfFolder"):
19
+ class _HfFolderShim:
20
+ path_token = None
21
+
22
+ @staticmethod
23
+ def get_token():
24
+ return (os.environ.get("HF_TOKEN")
25
+ or os.environ.get("HUGGING_FACE_HUB_TOKEN"))
26
+
27
+ @classmethod
28
+ def save_token(cls, token):
29
+ pass
30
+
31
+ @classmethod
32
+ def delete_token(cls):
33
+ pass
34
+
35
+ _hfh.HfFolder = _HfFolderShim
36
+ del _hfh
37
+
38
  # ZeroGPU: `spaces` MUST be imported before torch / any CUDA-related package,
39
  # otherwise it raises "CUDA has been initialized before importing the spaces
40
  # package". The mmcv bootstrap below imports torch, so import spaces first.
 
52
  sys.path.insert(0, REPO)
53
  sys.path.insert(0, os.path.join(REPO, "demo")) # for `import configs` inside helpers.py
54
 
55
+ # ── ensure mmcv-full (with CUDA ops) is importable ───────────────────────────
56
+ # HF's build phase can't compile mmcv (isolated pip, no Space Variables), so we
57
+ # handle it at runtime. Compiling from source takes ~9 min, so we cache the
58
+ # built wheel on the Hub keyed by torch/cuda/python and reuse it on later cold
59
+ # starts (download ~1 min). MMCV_CACHE_REPO must allow writes via HF_TOKEN.
60
+ MMCV_GIT = "git+https://github.com/open-mmlab/mmcv.git@v1.7.2"
61
+ MMCV_CACHE_REPO = os.environ.get("MMCV_CACHE_REPO", "ahmaddarkhalil/hoi-detr")
62
+
63
+
64
  def _ensure_mmcv():
65
  import shutil
66
+ import glob
67
+
68
  def run(cmd, env=None, check=True):
69
  print("[bootstrap] $", " ".join(cmd), flush=True)
70
  return subprocess.run(cmd, env=env, check=check)
71
 
72
+ def have_mmcv():
73
+ try:
74
+ import mmcv # noqa: F811
75
+ from mmcv.ops import RoIAlign # noqa: F401 β€” proves CUDA ops present
76
+ print(f"[bootstrap] mmcv {mmcv.__version__} (with ops) ready",
77
+ flush=True)
78
+ return True
79
+ except Exception:
80
+ return False
81
+
82
+ if have_mmcv():
83
  return
 
 
 
84
 
85
  import torch
86
+ tver = torch.__version__
87
+ cuver = (torch.version.cuda or "none")
88
+ pytag = f"cp{sys.version_info.major}{sys.version_info.minor}"
89
+ wheel_name = (f"mmcv_full-1.7.2-torch{tver.replace('+', '_')}-"
90
+ f"cu{cuver.replace('.', '')}-{pytag}-linux_x86_64.whl")
91
+ token = os.environ.get("HF_TOKEN")
92
+ print(f"[bootstrap] torch={tver} cuda={cuver}; cache wheel={wheel_name}",
93
+ flush=True)
94
+
95
+ # 1) Try a cached prebuilt wheel from the Hub.
96
+ try:
97
+ from huggingface_hub import hf_hub_download
98
+ whl = hf_hub_download(repo_id=MMCV_CACHE_REPO, filename=wheel_name,
99
+ token=token)
100
+ run([sys.executable, "-m", "pip", "install", whl])
101
+ if have_mmcv():
102
+ print("[bootstrap] installed cached mmcv wheel", flush=True)
103
+ return
104
+ except Exception as e:
105
+ print(f"[bootstrap] no usable cached wheel ({e!r}); building", flush=True)
106
+
107
+ # 2) Build from source.
108
+ print(f"[bootstrap] system nvcc: {shutil.which('nvcc')}", flush=True)
109
  run(["bash", "-lc", "gcc --version | head -1 || true"], check=False)
110
  print(f"[bootstrap] CUDA_HOME={os.environ.get('CUDA_HOME')}", flush=True)
 
111
  env = dict(os.environ)
112
  env["MMCV_WITH_OPS"] = "1"
113
  env["FORCE_CUDA"] = "1"
114
+ env.setdefault("TORCH_CUDA_ARCH_LIST", "12.0+PTX") # RTX Pro 6000 = sm_120
 
115
  env.setdefault("MAX_JOBS", "4")
116
+ outdir = "/tmp/mmcv_wheel"
117
+ run([sys.executable, "-m", "pip", "wheel", "--no-build-isolation",
118
+ "--no-deps", "-w", outdir, MMCV_GIT], env=env)
119
+ built = sorted(glob.glob(os.path.join(outdir, "mmcv_full-*.whl")))[0]
120
+ run([sys.executable, "-m", "pip", "install", built]) # also pulls addict
121
+ if not have_mmcv():
122
+ raise RuntimeError("mmcv built but import still fails")
123
+ print("[bootstrap] built mmcv from source", flush=True)
124
+
125
+ # 3) Cache the wheel for future cold starts (best-effort).
126
+ try:
127
+ if token:
128
+ from huggingface_hub import upload_file
129
+ upload_file(path_or_fileobj=built, path_in_repo=wheel_name,
130
+ repo_id=MMCV_CACHE_REPO, token=token,
131
+ commit_message="cache mmcv-full wheel")
132
+ print(f"[bootstrap] cached wheel -> {MMCV_CACHE_REPO}/{wheel_name}",
133
+ flush=True)
134
+ except Exception as e:
135
+ print(f"[bootstrap] wheel cache upload skipped ({e!r})", flush=True)
136
 
137
 
138
  _ensure_mmcv()
 
149
 
150
  import mmcv
151
  import numpy as np
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  import gradio as gr
153
  from huggingface_hub import hf_hub_download
154