File size: 3,927 Bytes
457b8fd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# 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