Launchpad automatic translations update.
[odoo/odoo.git] / bin / tools / threadinglocal.py
1 # -*- coding: utf-8 -*-
2 #Copyright (c) 2004-2005, CherryPy Team (team@cherrypy.org)
3 #All rights reserved.
4 #
5 #Redistribution and use in source and binary forms, with or without
6 #modification, are permitted provided that the following conditions are met:
7 #
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.
16 #
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.
27
28 # This is a backport of Python-2.4's threading.local() implementation
29
30 """Thread-local objects
31
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.)
36
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:
40
41   >>> mydata = local()
42   >>> mydata.number = 42
43   >>> mydata.number
44   42
45
46 You can also access the local-object's dictionary:
47
48   >>> mydata.__dict__
49   {'number': 42}
50   >>> mydata.__dict__.setdefault('widgets', [])
51   []
52   >>> mydata.widgets
53   []
54
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:
57
58   >>> log = []
59   >>> def f():
60   ...     items = mydata.__dict__.items()
61   ...     items.sort()
62   ...     log.append(items)
63   ...     mydata.number = 11
64   ...     log.append(mydata.number)
65
66   >>> import threading
67   >>> thread = threading.Thread(target=f)
68   >>> thread.start()
69   >>> thread.join()
70   >>> log
71   [[], 11]
72
73 we get different data.  Furthermore, changes made in the other thread
74 don't affect data seen in this thread:
75
76   >>> mydata.number
77   42
78
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
83 came from.
84
85 You can create custom local objects by subclassing the local class:
86
87   >>> class MyLocal(local):
88   ...     number = 2
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
97
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.
102
103 Now if we create a local object:
104
105   >>> mydata = MyLocal(color='red')
106
107 Now we have a default number:
108
109   >>> mydata.number
110   2
111
112 an initial color:
113
114   >>> mydata.color
115   'red'
116   >>> del mydata.color
117
118 And a method that operates on the data:
119
120   >>> mydata.squared()
121   4
122
123 As before, we can access the data in a separate thread:
124
125   >>> log = []
126   >>> thread = threading.Thread(target=f)
127   >>> thread.start()
128   >>> thread.join()
129   >>> log
130   [[('color', 'red'), ('initialized', True)], 11]
131
132 without affecting this thread's data:
133
134   >>> mydata.number
135   2
136   >>> mydata.color
137   Traceback (most recent call last):
138   ...
139   AttributeError: 'MyLocal' object has no attribute 'color'
140
141 Note that subclasses can define slots, but they are not thread
142 local. They are shared across threads:
143
144   >>> class MyLocal(local):
145   ...     __slots__ = 'number'
146
147   >>> mydata = MyLocal()
148   >>> mydata.number = 42
149   >>> mydata.color = 'red'
150
151 So, the separate thread:
152
153   >>> thread = threading.Thread(target=f)
154   >>> thread.start()
155   >>> thread.join()
156
157 affects what we see:
158
159   >>> mydata.number
160   11
161
162 >>> del mydata
163 """
164
165 # Threading import is at end
166
167 class _localbase(object):
168     __slots__ = '_local__key', '_local__args', '_local__lock'
169
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())
176
177         if args or kw and (cls.__init__ is object.__init__):
178             raise TypeError("Initialization arguments are not supported")
179
180         # We need to create the thread dict in anticipation of
181         # __init__ being called, to make sure we don't call it
182         # again ourselves.
183         dict = object.__getattribute__(self, '__dict__')
184         currentThread().__dict__[key] = dict
185
186         return self
187
188 def _patch(self):
189     key = object.__getattribute__(self, '_local__key')
190     d = currentThread().__dict__.get(key)
191     if d is None:
192         d = {}
193         currentThread().__dict__[key] = d
194         object.__setattr__(self, '__dict__', d)
195
196         # we have a new instance dict, so call out __init__ if we have
197         # one
198         cls = type(self)
199         if cls.__init__ is not object.__init__:
200             args, kw = object.__getattribute__(self, '_local__args')
201             cls.__init__(self, *args, **kw)
202     else:
203         object.__setattr__(self, '__dict__', d)
204
205 class local(_localbase):
206
207     def __getattribute__(self, name):
208         lock = object.__getattribute__(self, '_local__lock')
209         lock.acquire()
210         try:
211             _patch(self)
212             return object.__getattribute__(self, name)
213         finally:
214             lock.release()
215
216     def __setattr__(self, name, value):
217         lock = object.__getattribute__(self, '_local__lock')
218         lock.acquire()
219         try:
220             _patch(self)
221             return object.__setattr__(self, name, value)
222         finally:
223             lock.release()
224
225     def __delattr__(self, name):
226         lock = object.__getattribute__(self, '_local__lock')
227         lock.acquire()
228         try:
229             _patch(self)
230             return object.__delattr__(self, name)
231         finally:
232             lock.release()
233
234
235     def __del__():
236         threading_enumerate = enumerate
237         __getattribute__ = object.__getattribute__
238
239         def __del__(self):
240             key = __getattribute__(self, '_local__key')
241
242             try:
243                 threads = list(threading_enumerate())
244             except:
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
248                 return
249
250             for thread in threads:
251                 try:
252                     __dict__ = thread.__dict__
253                 except AttributeError:
254                     # Thread is dying, rest in peace
255                     continue
256
257                 if key in __dict__:
258                     try:
259                         del __dict__[key]
260                     except KeyError:
261                         pass # didn't have anything in this thread
262
263         return __del__
264     __del__ = __del__()
265
266 from threading import currentThread, enumerate, RLock
267
268 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
269