1
#Copyright (c) 2004-2005, CherryPy Team (team@cherrypy.org)
4
#Redistribution and use in source and binary forms, with or without
5
#modification, are permitted provided that the following conditions are met:
7
# * Redistributions of source code must retain the above copyright notice,
8
# this list of conditions and the following disclaimer.
9
# * Redistributions in binary form must reproduce the above copyright notice,
10
# this list of conditions and the following disclaimer in the documentation
11
# and/or other materials provided with the distribution.
12
# * Neither the name of the CherryPy Team nor the names of its contributors
13
# may be used to endorse or promote products derived from this software
14
# without specific prior written permission.
16
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
#DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20
#FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
#DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
#SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
#CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
#OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
#OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
# This is a backport of Python-2.4's threading.local() implementation
29
"""Thread-local objects
31
(Note that this module provides a Python version of thread
32
threading.local class. Depending on the version of Python you're
33
using, there may be a faster one available. You should always import
34
the local class from threading.)
36
Thread-local objects support the management of thread-local data.
37
If you have data that you want to be local to a thread, simply create
38
a thread-local object and use its attributes:
41
>>> mydata.number = 42
45
You can also access the local-object's dictionary:
49
>>> mydata.__dict__.setdefault('widgets', [])
54
What's important about thread-local objects is that their data are
55
local to a thread. If we access the data in a different thread:
59
... items = mydata.__dict__.items()
62
... mydata.number = 11
63
... log.append(mydata.number)
66
>>> thread = threading.Thread(target=f)
72
we get different data. Furthermore, changes made in the other thread
73
don't affect data seen in this thread:
78
Of course, values you get from a local object, including a __dict__
79
attribute, are for whatever thread was current at the time the
80
attribute was read. For that reason, you generally don't want to save
81
these values across threads, as they apply only to the thread they
84
You can create custom local objects by subclassing the local class:
86
>>> class MyLocal(local):
88
... initialized = False
89
... def __init__(self, **kw):
90
... if self.initialized:
91
... raise SystemError('__init__ called too many times')
92
... self.initialized = True
93
... self.__dict__.update(kw)
94
... def squared(self):
95
... return self.number ** 2
97
This can be useful to support default values, methods and
98
initialization. Note that if you define an __init__ method, it will be
99
called each time the local object is used in a separate thread. This
100
is necessary to initialize each thread's dictionary.
102
Now if we create a local object:
104
>>> mydata = MyLocal(color='red')
106
Now we have a default number:
117
And a method that operates on the data:
122
As before, we can access the data in a separate thread:
125
>>> thread = threading.Thread(target=f)
129
[[('color', 'red'), ('initialized', True)], 11]
131
without affecting this thread's data:
136
Traceback (most recent call last):
138
AttributeError: 'MyLocal' object has no attribute 'color'
140
Note that subclasses can define slots, but they are not thread
141
local. They are shared across threads:
143
>>> class MyLocal(local):
144
... __slots__ = 'number'
146
>>> mydata = MyLocal()
147
>>> mydata.number = 42
148
>>> mydata.color = 'red'
150
So, the separate thread:
152
>>> thread = threading.Thread(target=f)
164
# Threading import is at end
166
class _localbase(object):
167
__slots__ = '_local__key', '_local__args', '_local__lock'
169
def __new__(cls, *args, **kw):
170
self = object.__new__(cls)
171
key = '_local__key', 'thread.local.' + str(id(self))
172
object.__setattr__(self, '_local__key', key)
173
object.__setattr__(self, '_local__args', (args, kw))
174
object.__setattr__(self, '_local__lock', RLock())
176
if args or kw and (cls.__init__ is object.__init__):
177
raise TypeError("Initialization arguments are not supported")
179
# We need to create the thread dict in anticipation of
180
# __init__ being called, to make sure we don't call it
182
dict = object.__getattribute__(self, '__dict__')
183
currentThread().__dict__[key] = dict
188
key = object.__getattribute__(self, '_local__key')
189
d = currentThread().__dict__.get(key)
192
currentThread().__dict__[key] = d
193
object.__setattr__(self, '__dict__', d)
195
# we have a new instance dict, so call out __init__ if we have
198
if cls.__init__ is not object.__init__:
199
args, kw = object.__getattribute__(self, '_local__args')
200
cls.__init__(self, *args, **kw)
202
object.__setattr__(self, '__dict__', d)
204
class local(_localbase):
206
def __getattribute__(self, name):
207
lock = object.__getattribute__(self, '_local__lock')
211
return object.__getattribute__(self, name)
215
def __setattr__(self, name, value):
216
lock = object.__getattribute__(self, '_local__lock')
220
return object.__setattr__(self, name, value)
224
def __delattr__(self, name):
225
lock = object.__getattribute__(self, '_local__lock')
229
return object.__delattr__(self, name)
235
threading_enumerate = enumerate
236
__getattribute__ = object.__getattribute__
239
key = __getattribute__(self, '_local__key')
242
threads = list(threading_enumerate())
244
# if enumerate fails, as it seems to do during
245
# shutdown, we'll skip cleanup under the assumption
246
# that there is nothing to clean up
249
for thread in threads:
251
__dict__ = thread.__dict__
252
except AttributeError:
253
# Thread is dying, rest in peace
260
pass # didn't have anything in this thread
265
from threading import currentThread, enumerate, RLock