Glossarion / tqdm_safety.py
Shirochi's picture
Upload 41 files
457b8fd verified
raw
history blame
3.93 kB
# 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