1 # -*- coding: utf-8 -*-
2 #Copyright (c) 2004-2005, CherryPy Team (team@cherrypy.org)
5 #Redistribution and use in source and binary forms, with or without
6 #modification, are permitted provided that the following conditions are met:
8 # * Redistributions of source code must retain the above copyright notice,
9 # this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above copyright notice,
11 # this list of conditions and the following disclaimer in the documentation
12 # and/or other materials provided with the distribution.
13 # * Neither the name of the CherryPy Team nor the names of its contributors
14 # may be used to endorse or promote products derived from this software
15 # without specific prior written permission.
17 #THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 #ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 #WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 #DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 #FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 #DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 #SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 #CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 #OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 #OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 # This is a backport of Python-2.4's threading.local() implementation
30 """Thread-local objects
32 (Note that this module provides a Python version of thread
33 threading.local class. Depending on the version of Python you're
34 using, there may be a faster one available. You should always import
35 the local class from threading.)
37 Thread-local objects support the management of thread-local data.
38 If you have data that you want to be local to a thread, simply create
39 a thread-local object and use its attributes:
42 >>> mydata.number = 42
46 You can also access the local-object's dictionary:
50 >>> mydata.__dict__.setdefault('widgets', [])
55 What's important about thread-local objects is that their data are
56 local to a thread. If we access the data in a different thread:
60 ... items = mydata.__dict__.items()
63 ... mydata.number = 11
64 ... log.append(mydata.number)
67 >>> thread = threading.Thread(target=f)
73 we get different data. Furthermore, changes made in the other thread
74 don't affect data seen in this thread:
79 Of course, values you get from a local object, including a __dict__
80 attribute, are for whatever thread was current at the time the
81 attribute was read. For that reason, you generally don't want to save
82 these values across threads, as they apply only to the thread they
85 You can create custom local objects by subclassing the local class:
87 >>> class MyLocal(local):
89 ... initialized = False
90 ... def __init__(self, **kw):
91 ... if self.initialized:
92 ... raise SystemError('__init__ called too many times')
93 ... self.initialized = True
94 ... self.__dict__.update(kw)
95 ... def squared(self):
96 ... return self.number ** 2
98 This can be useful to support default values, methods and
99 initialization. Note that if you define an __init__ method, it will be
100 called each time the local object is used in a separate thread. This
101 is necessary to initialize each thread's dictionary.
103 Now if we create a local object:
105 >>> mydata = MyLocal(color='red')
107 Now we have a default number:
118 And a method that operates on the data:
123 As before, we can access the data in a separate thread:
126 >>> thread = threading.Thread(target=f)
130 [[('color', 'red'), ('initialized', True)], 11]
132 without affecting this thread's data:
137 Traceback (most recent call last):
139 AttributeError: 'MyLocal' object has no attribute 'color'
141 Note that subclasses can define slots, but they are not thread
142 local. They are shared across threads:
144 >>> class MyLocal(local):
145 ... __slots__ = 'number'
147 >>> mydata = MyLocal()
148 >>> mydata.number = 42
149 >>> mydata.color = 'red'
151 So, the separate thread:
153 >>> thread = threading.Thread(target=f)
165 # Threading import is at end
167 class _localbase(object):
168 __slots__ = '_local__key', '_local__args', '_local__lock'
170 def __new__(cls, *args, **kw):
171 self = object.__new__(cls)
172 key = '_local__key', 'thread.local.' + str(id(self))
173 object.__setattr__(self, '_local__key', key)
174 object.__setattr__(self, '_local__args', (args, kw))
175 object.__setattr__(self, '_local__lock', RLock())
177 if args or kw and (cls.__init__ is object.__init__):
178 raise TypeError("Initialization arguments are not supported")
180 # We need to create the thread dict in anticipation of
181 # __init__ being called, to make sure we don't call it
183 dict = object.__getattribute__(self, '__dict__')
184 currentThread().__dict__[key] = dict
189 key = object.__getattribute__(self, '_local__key')
190 d = currentThread().__dict__.get(key)
193 currentThread().__dict__[key] = d
194 object.__setattr__(self, '__dict__', d)
196 # we have a new instance dict, so call out __init__ if we have
199 if cls.__init__ is not object.__init__:
200 args, kw = object.__getattribute__(self, '_local__args')
201 cls.__init__(self, *args, **kw)
203 object.__setattr__(self, '__dict__', d)
205 class local(_localbase):
207 def __getattribute__(self, name):
208 lock = object.__getattribute__(self, '_local__lock')
212 return object.__getattribute__(self, name)
216 def __setattr__(self, name, value):
217 lock = object.__getattribute__(self, '_local__lock')
221 return object.__setattr__(self, name, value)
225 def __delattr__(self, name):
226 lock = object.__getattribute__(self, '_local__lock')
230 return object.__delattr__(self, name)
236 threading_enumerate = enumerate
237 __getattribute__ = object.__getattribute__
240 key = __getattribute__(self, '_local__key')
243 threads = list(threading_enumerate())
245 # if enumerate fails, as it seems to do during
246 # shutdown, we'll skip cleanup under the assumption
247 # that there is nothing to clean up
250 for thread in threads:
252 __dict__ = thread.__dict__
253 except AttributeError:
254 # Thread is dying, rest in peace
261 pass # didn't have anything in this thread
266 from threading import currentThread, enumerate, RLock
268 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: