add release date
[python/dscho.git] / Lib / _threading_local.py
blobe953597005eba7fd9312d7a6e098ee160d2ab95f
1 """Thread-local objects.
3 (Note that this module provides a Python version of the threading.local
4 class. Depending on the version of Python you're using, there may be a
5 faster one available. You should always import the `local` class from
6 `threading`.)
8 Thread-local objects support the management of thread-local data.
9 If you have data that you want to be local to a thread, simply create
10 a thread-local object and use its attributes:
12 >>> mydata = local()
13 >>> mydata.number = 42
14 >>> mydata.number
17 You can also access the local-object's dictionary:
19 >>> mydata.__dict__
20 {'number': 42}
21 >>> mydata.__dict__.setdefault('widgets', [])
23 >>> mydata.widgets
26 What's important about thread-local objects is that their data are
27 local to a thread. If we access the data in a different thread:
29 >>> log = []
30 >>> def f():
31 ... items = mydata.__dict__.items()
32 ... items.sort()
33 ... log.append(items)
34 ... mydata.number = 11
35 ... log.append(mydata.number)
37 >>> import threading
38 >>> thread = threading.Thread(target=f)
39 >>> thread.start()
40 >>> thread.join()
41 >>> log
42 [[], 11]
44 we get different data. Furthermore, changes made in the other thread
45 don't affect data seen in this thread:
47 >>> mydata.number
50 Of course, values you get from a local object, including a __dict__
51 attribute, are for whatever thread was current at the time the
52 attribute was read. For that reason, you generally don't want to save
53 these values across threads, as they apply only to the thread they
54 came from.
56 You can create custom local objects by subclassing the local class:
58 >>> class MyLocal(local):
59 ... number = 2
60 ... initialized = False
61 ... def __init__(self, **kw):
62 ... if self.initialized:
63 ... raise SystemError('__init__ called too many times')
64 ... self.initialized = True
65 ... self.__dict__.update(kw)
66 ... def squared(self):
67 ... return self.number ** 2
69 This can be useful to support default values, methods and
70 initialization. Note that if you define an __init__ method, it will be
71 called each time the local object is used in a separate thread. This
72 is necessary to initialize each thread's dictionary.
74 Now if we create a local object:
76 >>> mydata = MyLocal(color='red')
78 Now we have a default number:
80 >>> mydata.number
83 an initial color:
85 >>> mydata.color
86 'red'
87 >>> del mydata.color
89 And a method that operates on the data:
91 >>> mydata.squared()
94 As before, we can access the data in a separate thread:
96 >>> log = []
97 >>> thread = threading.Thread(target=f)
98 >>> thread.start()
99 >>> thread.join()
100 >>> log
101 [[('color', 'red'), ('initialized', True)], 11]
103 without affecting this thread's data:
105 >>> mydata.number
107 >>> mydata.color
108 Traceback (most recent call last):
110 AttributeError: 'MyLocal' object has no attribute 'color'
112 Note that subclasses can define slots, but they are not thread
113 local. They are shared across threads:
115 >>> class MyLocal(local):
116 ... __slots__ = 'number'
118 >>> mydata = MyLocal()
119 >>> mydata.number = 42
120 >>> mydata.color = 'red'
122 So, the separate thread:
124 >>> thread = threading.Thread(target=f)
125 >>> thread.start()
126 >>> thread.join()
128 affects what we see:
130 >>> mydata.number
133 >>> del mydata
136 __all__ = ["local"]
138 # We need to use objects from the threading module, but the threading
139 # module may also want to use our `local` class, if support for locals
140 # isn't compiled in to the `thread` module. This creates potential problems
141 # with circular imports. For that reason, we don't import `threading`
142 # until the bottom of this file (a hack sufficient to worm around the
143 # potential problems). Note that almost all platforms do have support for
144 # locals in the `thread` module, and there is no circular import problem
145 # then, so problems introduced by fiddling the order of imports here won't
146 # manifest on most boxes.
148 class _localbase(object):
149 __slots__ = '_local__key', '_local__args', '_local__lock'
151 def __new__(cls, *args, **kw):
152 self = object.__new__(cls)
153 key = '_local__key', 'thread.local.' + str(id(self))
154 object.__setattr__(self, '_local__key', key)
155 object.__setattr__(self, '_local__args', (args, kw))
156 object.__setattr__(self, '_local__lock', RLock())
158 if (args or kw) and (cls.__init__ is object.__init__):
159 raise TypeError("Initialization arguments are not supported")
161 # We need to create the thread dict in anticipation of
162 # __init__ being called, to make sure we don't call it
163 # again ourselves.
164 dict = object.__getattribute__(self, '__dict__')
165 current_thread().__dict__[key] = dict
167 return self
169 def _patch(self):
170 key = object.__getattribute__(self, '_local__key')
171 d = current_thread().__dict__.get(key)
172 if d is None:
173 d = {}
174 current_thread().__dict__[key] = d
175 object.__setattr__(self, '__dict__', d)
177 # we have a new instance dict, so call out __init__ if we have
178 # one
179 cls = type(self)
180 if cls.__init__ is not object.__init__:
181 args, kw = object.__getattribute__(self, '_local__args')
182 cls.__init__(self, *args, **kw)
183 else:
184 object.__setattr__(self, '__dict__', d)
186 class local(_localbase):
188 def __getattribute__(self, name):
189 lock = object.__getattribute__(self, '_local__lock')
190 lock.acquire()
191 try:
192 _patch(self)
193 return object.__getattribute__(self, name)
194 finally:
195 lock.release()
197 def __setattr__(self, name, value):
198 lock = object.__getattribute__(self, '_local__lock')
199 lock.acquire()
200 try:
201 _patch(self)
202 return object.__setattr__(self, name, value)
203 finally:
204 lock.release()
206 def __delattr__(self, name):
207 lock = object.__getattribute__(self, '_local__lock')
208 lock.acquire()
209 try:
210 _patch(self)
211 return object.__delattr__(self, name)
212 finally:
213 lock.release()
215 def __del__(self):
216 import threading
218 key = object.__getattribute__(self, '_local__key')
220 try:
221 # We use the non-locking API since we might already hold the lock
222 # (__del__ can be called at any point by the cyclic GC).
223 threads = threading._enumerate()
224 except:
225 # If enumerating the current threads fails, as it seems to do
226 # during shutdown, we'll skip cleanup under the assumption
227 # that there is nothing to clean up.
228 return
230 for thread in threads:
231 try:
232 __dict__ = thread.__dict__
233 except AttributeError:
234 # Thread is dying, rest in peace.
235 continue
237 if key in __dict__:
238 try:
239 del __dict__[key]
240 except KeyError:
241 pass # didn't have anything in this thread
243 from threading import current_thread, RLock