10c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi"""Thread-local objects. 20c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 30c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi(Note that this module provides a Python version of the threading.local 40c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi class. Depending on the version of Python you're using, there may be a 50c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi faster one available. You should always import the `local` class from 60c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi `threading`.) 70c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 80c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiThread-local objects support the management of thread-local data. 90c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiIf you have data that you want to be local to a thread, simply create 100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yia thread-local object and use its attributes: 110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> mydata = local() 130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> mydata.number = 42 140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> mydata.number 150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 42 160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiYou can also access the local-object's dictionary: 180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> mydata.__dict__ 200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi {'number': 42} 210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> mydata.__dict__.setdefault('widgets', []) 220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi [] 230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> mydata.widgets 240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi [] 250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiWhat's important about thread-local objects is that their data are 270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yilocal to a thread. If we access the data in a different thread: 280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> log = [] 300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> def f(): 310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ... items = mydata.__dict__.items() 320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ... items.sort() 330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ... log.append(items) 340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ... mydata.number = 11 350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ... log.append(mydata.number) 360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> import threading 380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> thread = threading.Thread(target=f) 390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> thread.start() 400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> thread.join() 410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> log 420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi [[], 11] 430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiwe get different data. Furthermore, changes made in the other thread 450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yidon't affect data seen in this thread: 460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> mydata.number 480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 42 490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiOf course, values you get from a local object, including a __dict__ 510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiattribute, are for whatever thread was current at the time the 520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiattribute was read. For that reason, you generally don't want to save 530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yithese values across threads, as they apply only to the thread they 540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yicame from. 550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiYou can create custom local objects by subclassing the local class: 570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> class MyLocal(local): 590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ... number = 2 600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ... initialized = False 610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ... def __init__(self, **kw): 620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ... if self.initialized: 630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ... raise SystemError('__init__ called too many times') 640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ... self.initialized = True 650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ... self.__dict__.update(kw) 660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ... def squared(self): 670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ... return self.number ** 2 680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiThis can be useful to support default values, methods and 700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiinitialization. Note that if you define an __init__ method, it will be 710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yicalled each time the local object is used in a separate thread. This 720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiis necessary to initialize each thread's dictionary. 730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiNow if we create a local object: 750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> mydata = MyLocal(color='red') 770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiNow we have a default number: 790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> mydata.number 810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2 820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yian initial color: 840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> mydata.color 860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 'red' 870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> del mydata.color 880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiAnd a method that operates on the data: 900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> mydata.squared() 920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 4 930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiAs before, we can access the data in a separate thread: 950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> log = [] 970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> thread = threading.Thread(target=f) 980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> thread.start() 990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> thread.join() 1000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> log 1010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi [[('color', 'red'), ('initialized', True)], 11] 1020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiwithout affecting this thread's data: 1040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> mydata.number 1060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2 1070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> mydata.color 1080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi Traceback (most recent call last): 1090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ... 1100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi AttributeError: 'MyLocal' object has no attribute 'color' 1110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiNote that subclasses can define slots, but they are not thread 1130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yilocal. They are shared across threads: 1140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> class MyLocal(local): 1160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi ... __slots__ = 'number' 1170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> mydata = MyLocal() 1190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> mydata.number = 42 1200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> mydata.color = 'red' 1210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiSo, the separate thread: 1230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> thread = threading.Thread(target=f) 1250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> thread.start() 1260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> thread.join() 1270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiaffects what we see: 1290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi >>> mydata.number 1310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 11 1320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi>>> del mydata 1340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi""" 1350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi__all__ = ["local"] 1370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# We need to use objects from the threading module, but the threading 1390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# module may also want to use our `local` class, if support for locals 1400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# isn't compiled in to the `thread` module. This creates potential problems 1410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# with circular imports. For that reason, we don't import `threading` 1420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# until the bottom of this file (a hack sufficient to worm around the 1430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# potential problems). Note that almost all platforms do have support for 1440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# locals in the `thread` module, and there is no circular import problem 1450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# then, so problems introduced by fiddling the order of imports here won't 1460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# manifest on most boxes. 1470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass _localbase(object): 1490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi __slots__ = '_local__key', '_local__args', '_local__lock' 1500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def __new__(cls, *args, **kw): 1520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self = object.__new__(cls) 1530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi key = '_local__key', 'thread.local.' + str(id(self)) 1540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi object.__setattr__(self, '_local__key', key) 1550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi object.__setattr__(self, '_local__args', (args, kw)) 1560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi object.__setattr__(self, '_local__lock', RLock()) 1570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if (args or kw) and (cls.__init__ is object.__init__): 1590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi raise TypeError("Initialization arguments are not supported") 1600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # We need to create the thread dict in anticipation of 1620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # __init__ being called, to make sure we don't call it 1630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # again ourselves. 1640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi dict = object.__getattribute__(self, '__dict__') 1650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi current_thread().__dict__[key] = dict 1660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return self 1680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yidef _patch(self): 1700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi key = object.__getattribute__(self, '_local__key') 1710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi d = current_thread().__dict__.get(key) 1720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if d is None: 1730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi d = {} 1740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi current_thread().__dict__[key] = d 1750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi object.__setattr__(self, '__dict__', d) 1760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # we have a new instance dict, so call out __init__ if we have 1780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # one 1790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi cls = type(self) 1800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if cls.__init__ is not object.__init__: 1810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi args, kw = object.__getattribute__(self, '_local__args') 1820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi cls.__init__(self, *args, **kw) 1830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi else: 1840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi object.__setattr__(self, '__dict__', d) 1850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass local(_localbase): 1870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def __getattribute__(self, name): 1890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi lock = object.__getattribute__(self, '_local__lock') 1900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi lock.acquire() 1910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 1920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi _patch(self) 1930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return object.__getattribute__(self, name) 1940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi finally: 1950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi lock.release() 1960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def __setattr__(self, name, value): 1980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if name == '__dict__': 1990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi raise AttributeError( 2000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi "%r object attribute '__dict__' is read-only" 2010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi % self.__class__.__name__) 2020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi lock = object.__getattribute__(self, '_local__lock') 2030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi lock.acquire() 2040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 2050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi _patch(self) 2060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return object.__setattr__(self, name, value) 2070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi finally: 2080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi lock.release() 2090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def __delattr__(self, name): 2110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if name == '__dict__': 2120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi raise AttributeError( 2130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi "%r object attribute '__dict__' is read-only" 2140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi % self.__class__.__name__) 2150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi lock = object.__getattribute__(self, '_local__lock') 2160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi lock.acquire() 2170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 2180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi _patch(self) 2190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return object.__delattr__(self, name) 2200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi finally: 2210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi lock.release() 2220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def __del__(self): 2240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi import threading 2250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi key = object.__getattribute__(self, '_local__key') 2270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 2290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # We use the non-locking API since we might already hold the lock 2300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # (__del__ can be called at any point by the cyclic GC). 2310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi threads = threading._enumerate() 2320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except: 2330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # If enumerating the current threads fails, as it seems to do 2340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # during shutdown, we'll skip cleanup under the assumption 2350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # that there is nothing to clean up. 2360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return 2370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi for thread in threads: 2390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 2400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi __dict__ = thread.__dict__ 2410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except AttributeError: 2420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Thread is dying, rest in peace. 2430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi continue 2440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if key in __dict__: 2460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 2470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi del __dict__[key] 2480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except KeyError: 2490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi pass # didn't have anything in this thread 2500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 2510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yifrom threading import current_thread, RLock 252