0001 """Thread-local objects 0002 0003 (Note that this module provides a Python version of thread 0004 threading.local class. Depending on the version of Python you're 0005 using, there may be a faster one available. You should always import 0006 the local class from threading.) 0007 0008 Thread-local objects support the management of thread-local data. 0009 If you have data that you want to be local to a thread, simply create 0010 a thread-local object and use its attributes: 0011 0012 >>> mydata = local() 0013 >>> mydata.number = 42 0014 >>> mydata.number 0015 42 0016 0017 You can also access the local-object's dictionary: 0018 0019 >>> mydata.__dict__ 0020 {'number': 42} 0021 >>> mydata.__dict__.setdefault('widgets', []) 0022 [] 0023 >>> mydata.widgets 0024 [] 0025 0026 What's important about thread-local objects is that their data are 0027 local to a thread. If we access the data in a different thread: 0028 0029 >>> log = [] 0030 >>> def f(): 0031 ... items = mydata.__dict__.items() 0032 ... items.sort() 0033 ... log.append(items) 0034 ... mydata.number = 11 0035 ... log.append(mydata.number) 0036 0037 >>> import threading 0038 >>> thread = threading.Thread(target=f) 0039 >>> thread.start() 0040 >>> thread.join() 0041 >>> log 0042 [[], 11] 0043 0044 we get different data. Furthermore, changes made in the other thread 0045 don't affect data seen in this thread: 0046 0047 >>> mydata.number 0048 42 0049 0050 Of course, values you get from a local object, including a __dict__ 0051 attribute, are for whatever thread was current at the time the 0052 attribute was read. For that reason, you generally don't want to save 0053 these values across threads, as they apply only to the thread they 0054 came from. 0055 0056 You can create custom local objects by subclassing the local class: 0057 0058 >>> class MyLocal(local): 0059 ... number = 2 0060 ... initialized = False 0061 ... def __init__(self, **kw): 0062 ... if self.initialized: 0063 ... raise SystemError('__init__ called too many times') 0064 ... self.initialized = True 0065 ... self.__dict__.update(kw) 0066 ... def squared(self): 0067 ... return self.number ** 2 0068 0069 This can be useful to support default values, methods and 0070 initialization. Note that if you define an __init__ method, it will be 0071 called each time the local object is used in a separate thread. This 0072 is necessary to initialize each thread's dictionary. 0073 0074 Now if we create a local object: 0075 0076 >>> mydata = MyLocal(color='red') 0077 0078 Now we have a default number: 0079 0080 >>> mydata.number 0081 2 0082 0083 an initial color: 0084 0085 >>> mydata.color 0086 'red' 0087 >>> del mydata.color 0088 0089 And a method that operates on the data: 0090 0091 >>> mydata.squared() 0092 4 0093 0094 As before, we can access the data in a separate thread: 0095 0096 >>> log = [] 0097 >>> thread = threading.Thread(target=f) 0098 >>> thread.start() 0099 >>> thread.join() 0100 >>> log 0101 [[('color', 'red'), ('initialized', True)], 11] 0102 0103 without affecting this thread's data: 0104 0105 >>> mydata.number 0106 2 0107 >>> mydata.color 0108 Traceback (most recent call last): 0109 ... 0110 AttributeError: 'MyLocal' object has no attribute 'color' 0111 0112 Note that subclasses can define slots, but they are not thread 0113 local. They are shared across threads: 0114 0115 >>> class MyLocal(local): 0116 ... __slots__ = 'number' 0117 0118 >>> mydata = MyLocal() 0119 >>> mydata.number = 42 0120 >>> mydata.color = 'red' 0121 0122 So, the separate thread: 0123 0124 >>> thread = threading.Thread(target=f) 0125 >>> thread.start() 0126 >>> thread.join() 0127 0128 affects what we see: 0129 0130 >>> mydata.number 0131 11 0132 0133 >>> del mydata 0134 """ 0135 0136 # Threading import is at end 0137 0138 class _localbase(object): 0139 __slots__ = '_local__key', '_local__args', '_local__lock' 0140 0141 def __new__(cls, *args, **kw): 0142 self = object.__new__(cls) 0143 key = '_local__key', 'thread.local.' + str(id(self)) 0144 object.__setattr__(self, '_local__key', key) 0145 object.__setattr__(self, '_local__args', (args, kw)) 0146 object.__setattr__(self, '_local__lock', RLock()) 0147 0148 if args or kw and (cls.__init__ is object.__init__): 0149 raise TypeError("Initialization arguments are not supported") 0150 0151 # We need to create the thread dict in anticipation of 0152 # __init__ being called, to make sire we don't cal it 0153 # again ourselves. 0154 dict = object.__getattribute__(self, '__dict__') 0155 currentThread().__dict__[key] = dict 0156 0157 return self 0158 0159 def _patch(self): 0160 key = object.__getattribute__(self, '_local__key') 0161 d = currentThread().__dict__.get(key) 0162 if d is None: 0163 d = {} 0164 currentThread().__dict__[key] = d 0165 object.__setattr__(self, '__dict__', d) 0166 0167 # we have a new instance dict, so call out __init__ if we have 0168 # one 0169 cls = type(self) 0170 if cls.__init__ is not object.__init__: 0171 args, kw = object.__getattribute__(self, '_local__args') 0172 cls.__init__(self, *args, **kw) 0173 else: 0174 object.__setattr__(self, '__dict__', d) 0175 0176 class local(_localbase): 0177 0178 def __getattribute__(self, name): 0179 lock = object.__getattribute__(self, '_local__lock') 0180 lock.acquire() 0181 try: 0182 _patch(self) 0183 return object.__getattribute__(self, name) 0184 finally: 0185 lock.release() 0186 0187 def __setattr__(self, name, value): 0188 lock = object.__getattribute__(self, '_local__lock') 0189 lock.acquire() 0190 try: 0191 _patch(self) 0192 return object.__setattr__(self, name, value) 0193 finally: 0194 lock.release() 0195 0196 def __delattr__(self, name): 0197 lock = object.__getattribute__(self, '_local__lock') 0198 lock.acquire() 0199 try: 0200 _patch(self) 0201 return object.__delattr__(self, name) 0202 finally: 0203 lock.release() 0204 0205 0206 def __del__(): 0207 threading_enumerate = enumerate 0208 __getattribute__ = object.__getattribute__ 0209 0210 def __del__(self): 0211 key = __getattribute__(self, '_local__key') 0212 0213 try: 0214 threads = list(threading_enumerate()) 0215 except: 0216 # if enumerate fails, as it seems to do during 0217 # shutdown, we'll skip cleanup under the assumption 0218 # that there is nothing to clean up 0219 return 0220 0221 for thread in threads: 0222 try: 0223 __dict__ = thread.__dict__ 0224 except AttributeError: 0225 # Thread is dying, rest in peace 0226 continue 0227 0228 if key in __dict__: 0229 try: 0230 del __dict__[key] 0231 except KeyError: 0232 pass # didn't have anything in this thread 0233 0234 return __del__ 0235 __del__ = __del__() 0236 0237 from threading import currentThread, enumerate, RLock 0238
Generated by PyXR 0.9.4