blob: 09a3515bdb92b40b1e43caa0cca25f0da5981aae [file] [log] [blame]
Tim Peterse247e892006-04-18 03:28:32 +00001"""Thread-local objects.
Jim Fultond15dc062004-07-14 19:11:50 +00002
Tim Peterse247e892006-04-18 03:28:32 +00003(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`.)
Jim Fultond15dc062004-07-14 19:11:50 +00007
8Thread-local objects support the management of thread-local data.
9If you have data that you want to be local to a thread, simply create
Andrew M. Kuchling3fc2fde2004-07-15 12:17:26 +000010a thread-local object and use its attributes:
Jim Fultond15dc062004-07-14 19:11:50 +000011
12 >>> mydata = local()
13 >>> mydata.number = 42
14 >>> mydata.number
15 42
16
17You can also access the local-object's dictionary:
18
19 >>> mydata.__dict__
20 {'number': 42}
21 >>> mydata.__dict__.setdefault('widgets', [])
22 []
23 >>> mydata.widgets
24 []
25
26What's important about thread-local objects is that their data are
27local to a thread. If we access the data in a different thread:
28
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)
36
37 >>> import threading
38 >>> thread = threading.Thread(target=f)
39 >>> thread.start()
40 >>> thread.join()
41 >>> log
42 [[], 11]
43
44we get different data. Furthermore, changes made in the other thread
45don't affect data seen in this thread:
46
47 >>> mydata.number
48 42
49
50Of course, values you get from a local object, including a __dict__
51attribute, are for whatever thread was current at the time the
52attribute was read. For that reason, you generally don't want to save
53these values across threads, as they apply only to the thread they
54came from.
55
56You can create custom local objects by subclassing the local class:
57
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
68
69This can be useful to support default values, methods and
70initialization. Note that if you define an __init__ method, it will be
71called each time the local object is used in a separate thread. This
72is necessary to initialize each thread's dictionary.
73
74Now if we create a local object:
75
76 >>> mydata = MyLocal(color='red')
77
78Now we have a default number:
79
80 >>> mydata.number
81 2
82
83an initial color:
Tim Peters182b5ac2004-07-18 06:16:08 +000084
Jim Fultond15dc062004-07-14 19:11:50 +000085 >>> mydata.color
86 'red'
87 >>> del mydata.color
88
89And a method that operates on the data:
90
91 >>> mydata.squared()
92 4
93
94As before, we can access the data in a separate thread:
95
96 >>> log = []
97 >>> thread = threading.Thread(target=f)
98 >>> thread.start()
99 >>> thread.join()
100 >>> log
101 [[('color', 'red'), ('initialized', True)], 11]
102
Andrew M. Kuchling3fc2fde2004-07-15 12:17:26 +0000103without affecting this thread's data:
Jim Fultond15dc062004-07-14 19:11:50 +0000104
105 >>> mydata.number
106 2
107 >>> mydata.color
108 Traceback (most recent call last):
109 ...
110 AttributeError: 'MyLocal' object has no attribute 'color'
111
112Note that subclasses can define slots, but they are not thread
113local. They are shared across threads:
114
115 >>> class MyLocal(local):
116 ... __slots__ = 'number'
117
118 >>> mydata = MyLocal()
119 >>> mydata.number = 42
120 >>> mydata.color = 'red'
121
122So, the separate thread:
123
124 >>> thread = threading.Thread(target=f)
125 >>> thread.start()
126 >>> thread.join()
127
128affects what we see:
129
130 >>> mydata.number
131 11
132
133>>> del mydata
134"""
135
Tim Peterse247e892006-04-18 03:28:32 +0000136__all__ = ["local"]
137
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.
Jim Fultond15dc062004-07-14 19:11:50 +0000147
148class _localbase(object):
149 __slots__ = '_local__key', '_local__args', '_local__lock'
150
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())
157
Jack Diederich1ce61362010-02-22 19:55:22 +0000158 if (args or kw) and (cls.__init__ is object.__init__):
Jim Fultond15dc062004-07-14 19:11:50 +0000159 raise TypeError("Initialization arguments are not supported")
160
161 # We need to create the thread dict in anticipation of
Neal Norwitz7025ce62005-11-25 02:02:50 +0000162 # __init__ being called, to make sure we don't call it
Jim Fultond15dc062004-07-14 19:11:50 +0000163 # again ourselves.
164 dict = object.__getattribute__(self, '__dict__')
Benjamin Peterson0fbcf692008-06-11 17:27:50 +0000165 current_thread().__dict__[key] = dict
Jim Fultond15dc062004-07-14 19:11:50 +0000166
167 return self
168
169def _patch(self):
170 key = object.__getattribute__(self, '_local__key')
Benjamin Peterson0fbcf692008-06-11 17:27:50 +0000171 d = current_thread().__dict__.get(key)
Jim Fultond15dc062004-07-14 19:11:50 +0000172 if d is None:
173 d = {}
Benjamin Peterson0fbcf692008-06-11 17:27:50 +0000174 current_thread().__dict__[key] = d
Jim Fultond15dc062004-07-14 19:11:50 +0000175 object.__setattr__(self, '__dict__', d)
176
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)
185
186class local(_localbase):
187
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()
196
197 def __setattr__(self, name, value):
Antoine Pitroua4083502010-08-28 18:29:13 +0000198 if name == '__dict__':
199 raise AttributeError(
200 "%r object attribute '__dict__' is read-only"
201 % self.__class__.__name__)
Jim Fultond15dc062004-07-14 19:11:50 +0000202 lock = object.__getattribute__(self, '_local__lock')
203 lock.acquire()
204 try:
205 _patch(self)
206 return object.__setattr__(self, name, value)
207 finally:
208 lock.release()
209
210 def __delattr__(self, name):
Antoine Pitroua4083502010-08-28 18:29:13 +0000211 if name == '__dict__':
212 raise AttributeError(
213 "%r object attribute '__dict__' is read-only"
214 % self.__class__.__name__)
Jim Fultond15dc062004-07-14 19:11:50 +0000215 lock = object.__getattribute__(self, '_local__lock')
216 lock.acquire()
217 try:
218 _patch(self)
219 return object.__delattr__(self, name)
220 finally:
221 lock.release()
222
Tim Peterse247e892006-04-18 03:28:32 +0000223 def __del__(self):
224 import threading
Jim Fultond15dc062004-07-14 19:11:50 +0000225
Tim Petersc7605f22006-04-17 21:12:33 +0000226 key = object.__getattribute__(self, '_local__key')
Jim Fultond15dc062004-07-14 19:11:50 +0000227
Tim Petersc7605f22006-04-17 21:12:33 +0000228 try:
Antoine Pitrou99c160b2009-11-05 13:42:29 +0000229 # We use the non-locking API since we might already hold the lock
230 # (__del__ can be called at any point by the cyclic GC).
231 threads = threading._enumerate()
Tim Petersc7605f22006-04-17 21:12:33 +0000232 except:
Antoine Pitrou99c160b2009-11-05 13:42:29 +0000233 # If enumerating the current threads fails, as it seems to do
234 # during shutdown, we'll skip cleanup under the assumption
Tim Petersc7605f22006-04-17 21:12:33 +0000235 # that there is nothing to clean up.
236 return
237
238 for thread in threads:
Jim Fultond15dc062004-07-14 19:11:50 +0000239 try:
Tim Petersc7605f22006-04-17 21:12:33 +0000240 __dict__ = thread.__dict__
241 except AttributeError:
242 # Thread is dying, rest in peace.
243 continue
Jim Fultond15dc062004-07-14 19:11:50 +0000244
Tim Petersc7605f22006-04-17 21:12:33 +0000245 if key in __dict__:
Jim Fultond15dc062004-07-14 19:11:50 +0000246 try:
Tim Petersc7605f22006-04-17 21:12:33 +0000247 del __dict__[key]
248 except KeyError:
249 pass # didn't have anything in this thread
Tim Peterse247e892006-04-18 03:28:32 +0000250
Benjamin Peterson0fbcf692008-06-11 17:27:50 +0000251from threading import current_thread, RLock