blob: 1480329fde8b1ef19169e0c52c429f592fd01b60 [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
Jim Fultond15dc062004-07-14 19:11:50 +000060 ... def __init__(self, **kw):
Jim Fultond15dc062004-07-14 19:11:50 +000061 ... self.__dict__.update(kw)
62 ... def squared(self):
63 ... return self.number ** 2
64
65This can be useful to support default values, methods and
66initialization. Note that if you define an __init__ method, it will be
67called each time the local object is used in a separate thread. This
68is necessary to initialize each thread's dictionary.
69
70Now if we create a local object:
71
72 >>> mydata = MyLocal(color='red')
73
74Now we have a default number:
75
76 >>> mydata.number
77 2
78
79an initial color:
Tim Peters182b5ac2004-07-18 06:16:08 +000080
Jim Fultond15dc062004-07-14 19:11:50 +000081 >>> mydata.color
82 'red'
83 >>> del mydata.color
84
85And a method that operates on the data:
86
87 >>> mydata.squared()
88 4
89
90As before, we can access the data in a separate thread:
91
92 >>> log = []
93 >>> thread = threading.Thread(target=f)
94 >>> thread.start()
95 >>> thread.join()
96 >>> log
Miss Islington (bot)07c13ee2018-02-25 07:34:46 -080097 [[('color', 'red')], 11]
Jim Fultond15dc062004-07-14 19:11:50 +000098
Andrew M. Kuchling3fc2fde2004-07-15 12:17:26 +000099without affecting this thread's data:
Jim Fultond15dc062004-07-14 19:11:50 +0000100
101 >>> mydata.number
102 2
103 >>> mydata.color
104 Traceback (most recent call last):
105 ...
106 AttributeError: 'MyLocal' object has no attribute 'color'
107
108Note that subclasses can define slots, but they are not thread
109local. They are shared across threads:
110
111 >>> class MyLocal(local):
112 ... __slots__ = 'number'
113
114 >>> mydata = MyLocal()
115 >>> mydata.number = 42
116 >>> mydata.color = 'red'
117
118So, the separate thread:
119
120 >>> thread = threading.Thread(target=f)
121 >>> thread.start()
122 >>> thread.join()
123
124affects what we see:
125
126 >>> mydata.number
127 11
128
129>>> del mydata
130"""
131
Tim Peterse247e892006-04-18 03:28:32 +0000132__all__ = ["local"]
133
134# We need to use objects from the threading module, but the threading
135# module may also want to use our `local` class, if support for locals
136# isn't compiled in to the `thread` module. This creates potential problems
137# with circular imports. For that reason, we don't import `threading`
138# until the bottom of this file (a hack sufficient to worm around the
139# potential problems). Note that almost all platforms do have support for
140# locals in the `thread` module, and there is no circular import problem
141# then, so problems introduced by fiddling the order of imports here won't
142# manifest on most boxes.
Jim Fultond15dc062004-07-14 19:11:50 +0000143
144class _localbase(object):
145 __slots__ = '_local__key', '_local__args', '_local__lock'
146
147 def __new__(cls, *args, **kw):
148 self = object.__new__(cls)
149 key = '_local__key', 'thread.local.' + str(id(self))
150 object.__setattr__(self, '_local__key', key)
151 object.__setattr__(self, '_local__args', (args, kw))
152 object.__setattr__(self, '_local__lock', RLock())
153
Jack Diederich1ce61362010-02-22 19:55:22 +0000154 if (args or kw) and (cls.__init__ is object.__init__):
Jim Fultond15dc062004-07-14 19:11:50 +0000155 raise TypeError("Initialization arguments are not supported")
156
157 # We need to create the thread dict in anticipation of
Neal Norwitz7025ce62005-11-25 02:02:50 +0000158 # __init__ being called, to make sure we don't call it
Jim Fultond15dc062004-07-14 19:11:50 +0000159 # again ourselves.
160 dict = object.__getattribute__(self, '__dict__')
Benjamin Peterson0fbcf692008-06-11 17:27:50 +0000161 current_thread().__dict__[key] = dict
Jim Fultond15dc062004-07-14 19:11:50 +0000162
163 return self
164
165def _patch(self):
166 key = object.__getattribute__(self, '_local__key')
Benjamin Peterson0fbcf692008-06-11 17:27:50 +0000167 d = current_thread().__dict__.get(key)
Jim Fultond15dc062004-07-14 19:11:50 +0000168 if d is None:
169 d = {}
Benjamin Peterson0fbcf692008-06-11 17:27:50 +0000170 current_thread().__dict__[key] = d
Jim Fultond15dc062004-07-14 19:11:50 +0000171 object.__setattr__(self, '__dict__', d)
172
173 # we have a new instance dict, so call out __init__ if we have
174 # one
175 cls = type(self)
176 if cls.__init__ is not object.__init__:
177 args, kw = object.__getattribute__(self, '_local__args')
178 cls.__init__(self, *args, **kw)
179 else:
180 object.__setattr__(self, '__dict__', d)
181
182class local(_localbase):
183
184 def __getattribute__(self, name):
185 lock = object.__getattribute__(self, '_local__lock')
186 lock.acquire()
187 try:
188 _patch(self)
189 return object.__getattribute__(self, name)
190 finally:
191 lock.release()
192
193 def __setattr__(self, name, value):
Antoine Pitroua4083502010-08-28 18:29:13 +0000194 if name == '__dict__':
195 raise AttributeError(
196 "%r object attribute '__dict__' is read-only"
197 % self.__class__.__name__)
Jim Fultond15dc062004-07-14 19:11:50 +0000198 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()
205
206 def __delattr__(self, name):
Antoine Pitroua4083502010-08-28 18:29:13 +0000207 if name == '__dict__':
208 raise AttributeError(
209 "%r object attribute '__dict__' is read-only"
210 % self.__class__.__name__)
Jim Fultond15dc062004-07-14 19:11:50 +0000211 lock = object.__getattribute__(self, '_local__lock')
212 lock.acquire()
213 try:
214 _patch(self)
215 return object.__delattr__(self, name)
216 finally:
217 lock.release()
218
Tim Peterse247e892006-04-18 03:28:32 +0000219 def __del__(self):
220 import threading
Jim Fultond15dc062004-07-14 19:11:50 +0000221
Tim Petersc7605f22006-04-17 21:12:33 +0000222 key = object.__getattribute__(self, '_local__key')
Jim Fultond15dc062004-07-14 19:11:50 +0000223
Tim Petersc7605f22006-04-17 21:12:33 +0000224 try:
Antoine Pitrou99c160b2009-11-05 13:42:29 +0000225 # We use the non-locking API since we might already hold the lock
226 # (__del__ can be called at any point by the cyclic GC).
227 threads = threading._enumerate()
Tim Petersc7605f22006-04-17 21:12:33 +0000228 except:
Antoine Pitrou99c160b2009-11-05 13:42:29 +0000229 # If enumerating the current threads fails, as it seems to do
230 # during shutdown, we'll skip cleanup under the assumption
Tim Petersc7605f22006-04-17 21:12:33 +0000231 # that there is nothing to clean up.
232 return
233
234 for thread in threads:
Jim Fultond15dc062004-07-14 19:11:50 +0000235 try:
Tim Petersc7605f22006-04-17 21:12:33 +0000236 __dict__ = thread.__dict__
237 except AttributeError:
238 # Thread is dying, rest in peace.
239 continue
Jim Fultond15dc062004-07-14 19:11:50 +0000240
Tim Petersc7605f22006-04-17 21:12:33 +0000241 if key in __dict__:
Jim Fultond15dc062004-07-14 19:11:50 +0000242 try:
Tim Petersc7605f22006-04-17 21:12:33 +0000243 del __dict__[key]
244 except KeyError:
245 pass # didn't have anything in this thread
Tim Peterse247e892006-04-18 03:28:32 +0000246
Benjamin Peterson0fbcf692008-06-11 17:27:50 +0000247from threading import current_thread, RLock