goumsss Claude Sonnet 4.6 commited on
Commit
1bcd3f8
Β·
1 Parent(s): 2ed1759

Fix corrupt cache detection (lexists) and suppress asyncio GC noise

Browse files

Two robustness fixes in the HF Spaces startup block:

1. Use os.path.lexists() instead of os.path.exists() to detect corrupt
cache entries. exists() follows symlinks and returns False for broken
symlinks; lexists() checks the path itself β€” catches regular files,
working symlinks, AND broken symlinks. Also log exactly what was found
(dir / file / symlink / absent) to make future issues diagnosable.
Use os.unlink() which handles both files and symlinks (os.remove()
only handles files).

2. Install sys.unraisablehook to silently drop the Python 3.10 GC bug
(BaseEventLoop.__del__ β†’ "Invalid file descriptor: -1"). This is the
correct Python 3.8+ API for intercepting exceptions raised in __del__
and other un-raisable contexts β€” cleaner than monkey-patching __del__.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (1) hide show
  1. image_generator.py +27 -10
image_generator.py CHANGED
@@ -34,28 +34,45 @@ if not hasattr(torch, "xpu"):
34
  IS_HF_SPACE = os.environ.get("SPACE_ID") is not None
35
  if IS_HF_SPACE:
36
  import spaces
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  # Download model files to persistent storage at startup.
38
  # /data survives rebuilds β†’ only downloads once ever.
39
  _hf_token = os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN")
40
  _cache_dir = os.environ.get("HF_HUB_CACHE")
41
  print(f"Pre-caching FLUX.1-schnell β†’ {_cache_dir} (token={'set' if _hf_token else 'NOT SET'})")
42
  try:
43
- # Fix corrupt cache: if the model directory exists as a FILE (not a dir),
44
- # remove it so huggingface_hub can create the proper directory structure.
45
- import shutil
46
  _model_cache = os.path.join(_cache_dir, "models--black-forest-labs--FLUX.1-schnell")
47
- if os.path.exists(_model_cache) and not os.path.isdir(_model_cache):
48
- print(f"⚠️ Removing corrupt cache entry (not a directory): {_model_cache}")
49
- os.remove(_model_cache)
 
 
 
 
 
 
50
 
51
  from huggingface_hub import snapshot_download
52
- import threading, asyncio
53
 
54
  _result = {}
55
  def _download():
56
- # Create and own an explicit event loop for this thread so it can be
57
- # closed cleanly before the thread exits β€” prevents Python 3.10's
58
- # BaseEventLoop.__del__ from crashing with "Invalid file descriptor: -1".
59
  loop = asyncio.new_event_loop()
60
  asyncio.set_event_loop(loop)
61
  try:
 
34
  IS_HF_SPACE = os.environ.get("SPACE_ID") is not None
35
  if IS_HF_SPACE:
36
  import spaces
37
+ import sys, threading, asyncio
38
+
39
+ # ── Suppress Python 3.10 asyncio GC bug ──────────────────────────────
40
+ # BaseEventLoop.__del__ crashes with "Invalid file descriptor: -1" when
41
+ # an event loop is collected by the GC. sys.unraisablehook is the correct
42
+ # Python 3.8+ way to intercept these GC-triggered exceptions.
43
+ _orig_unraisable = sys.unraisablehook
44
+ def _unraisable_hook(args):
45
+ if args.exc_type is ValueError and "Invalid file descriptor" in str(args.exc_value):
46
+ return # silently drop β€” harmless Python 3.10 GC bug
47
+ _orig_unraisable(args)
48
+ sys.unraisablehook = _unraisable_hook
49
+
50
  # Download model files to persistent storage at startup.
51
  # /data survives rebuilds β†’ only downloads once ever.
52
  _hf_token = os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN")
53
  _cache_dir = os.environ.get("HF_HUB_CACHE")
54
  print(f"Pre-caching FLUX.1-schnell β†’ {_cache_dir} (token={'set' if _hf_token else 'NOT SET'})")
55
  try:
56
+ # Diagnose and fix corrupt cache entry.
57
+ # os.path.exists() follows symlinks β€” returns False for broken symlinks.
58
+ # os.path.lexists() checks the path itself β€” catches files AND broken symlinks.
59
  _model_cache = os.path.join(_cache_dir, "models--black-forest-labs--FLUX.1-schnell")
60
+ if os.path.lexists(_model_cache):
61
+ if os.path.isdir(_model_cache):
62
+ print(f"Cache dir OK: {_model_cache}")
63
+ else:
64
+ kind = "symlink" if os.path.islink(_model_cache) else "file"
65
+ print(f"⚠️ Removing corrupt cache entry ({kind}): {_model_cache}")
66
+ os.unlink(_model_cache) # works for both files and symlinks
67
+ else:
68
+ print(f"Cache entry absent β€” will be created by snapshot_download")
69
 
70
  from huggingface_hub import snapshot_download
 
71
 
72
  _result = {}
73
  def _download():
74
+ # Own an explicit event loop so it can be closed cleanly before the
75
+ # thread exits β€” prevents the GC from seeing an open loop.
 
76
  loop = asyncio.new_event_loop()
77
  asyncio.set_event_loop(loop)
78
  try: