Spaces:
Running
Running
| # tqdm_safety.py | |
| """ | |
| A defensive patch for tqdm to prevent AttributeError at interpreter shutdown: | |
| AttributeError: type object 'tqdm' has no attribute '_lock' | |
| Root cause | |
| - During interpreter shutdown, module globals/class attributes may be cleared before tqdm.__del__ runs. | |
| - tqdm.close() calls a class method that uses cls._lock; if it's already deleted, AttributeError is raised. | |
| Fix | |
| - Ensure a class-level _lock exists and is a threading.RLock(). | |
| - Wrap __del__ and close() to guard against shutdown-time attribute loss. | |
| - No-ops if core attributes are missing, preserving normal behavior during runtime. | |
| This keeps tqdm enabled and visible; it only avoids the noisy traceback on exit. | |
| """ | |
| from __future__ import annotations | |
| import threading | |
| def apply_tqdm_safety_patch() -> None: | |
| try: | |
| import tqdm as _tqdm_mod | |
| # Prefer the tqdm.tqdm class | |
| tqdm_cls = getattr(_tqdm_mod, 'tqdm', None) | |
| if tqdm_cls is None: | |
| # Some variants might expose TqdmExperimentalWarning only; bail quietly | |
| return | |
| # Ensure a class-level lock exists | |
| if not hasattr(tqdm_cls, '_lock') or getattr(tqdm_cls, '_lock') is None: | |
| try: | |
| tqdm_cls._lock = threading.RLock() | |
| except Exception: | |
| # As last resort, set a dummy object with context manager protocol | |
| class _DummyLock: | |
| def __enter__(self): | |
| return self | |
| def __exit__(self, exc_type, exc, tb): | |
| return False | |
| tqdm_cls._lock = _DummyLock() | |
| # Patch the class method used during close to guard missing attributes | |
| _orig_decr = getattr(tqdm_cls, '_decr_instances', None) | |
| if callable(_orig_decr): | |
| def _safe_decr_instances(*args, **kwargs): | |
| try: | |
| # cls._lock might be gone at shutdown | |
| if not hasattr(tqdm_cls, '_lock') or tqdm_cls._lock is None: | |
| return | |
| return _orig_decr(*args, **kwargs) | |
| except Exception: | |
| # Swallow shutdown-time errors only | |
| return | |
| try: | |
| _safe_decr_instances.__name__ = _orig_decr.__name__ | |
| except Exception: | |
| pass | |
| setattr(tqdm_cls, '_decr_instances', staticmethod(_safe_decr_instances)) | |
| # Wrap instance .close() to be defensive | |
| _orig_close = getattr(tqdm_cls, 'close', None) | |
| if callable(_orig_close): | |
| def _safe_close(self, *args, **kwargs): | |
| try: | |
| return _orig_close(self, *args, **kwargs) | |
| except AttributeError: | |
| # Happens if class attrs are missing at shutdown | |
| return | |
| except Exception: | |
| # Avoid raising during shutdown | |
| try: | |
| # Best effort: clear display without relying on internals | |
| fp = getattr(self, 'fp', None) | |
| if fp and hasattr(fp, 'flush'): | |
| fp.flush() | |
| except Exception: | |
| pass | |
| return | |
| setattr(tqdm_cls, 'close', _safe_close) | |
| # Wrap destructor to ignore shutdown-time errors | |
| _orig_del = getattr(tqdm_cls, '__del__', None) | |
| if callable(_orig_del): | |
| def _safe_del(self): | |
| try: | |
| _orig_del(self) | |
| except Exception: | |
| # Ignore any errors during interpreter shutdown | |
| return | |
| setattr(tqdm_cls, '__del__', _safe_del) | |
| except Exception: | |
| # Never let the safety patch break startup | |
| return | |