source file: /Library/Python/2.3/site-packages/CherryPy-3.0.1-py2.3.egg/cherrypy/_cpthreadinglocal.py
file stats: 70 lines, 46 executed: 65.7% covered
   1. # This is a backport of Python-2.4's threading.local() implementation
   2. 
   3. """Thread-local objects
   4. 
   5. (Note that this module provides a Python version of thread
   6.  threading.local class.  Depending on the version of Python you're
   7.  using, there may be a faster one available.  You should always import
   8.  the local class from threading.)
   9. 
  10. Thread-local objects support the management of thread-local data.
  11. If you have data that you want to be local to a thread, simply create
  12. a thread-local object and use its attributes:
  13. 
  14.   >>> mydata = local()
  15.   >>> mydata.number = 42
  16.   >>> mydata.number
  17.   42
  18. 
  19. You can also access the local-object's dictionary:
  20. 
  21.   >>> mydata.__dict__
  22.   {'number': 42}
  23.   >>> mydata.__dict__.setdefault('widgets', [])
  24.   []
  25.   >>> mydata.widgets
  26.   []
  27. 
  28. What's important about thread-local objects is that their data are
  29. local to a thread. If we access the data in a different thread:
  30. 
  31.   >>> log = []
  32.   >>> def f():
  33.   ...     items = mydata.__dict__.items()
  34.   ...     items.sort()
  35.   ...     log.append(items)
  36.   ...     mydata.number = 11
  37.   ...     log.append(mydata.number)
  38. 
  39.   >>> import threading
  40.   >>> thread = threading.Thread(target=f)
  41.   >>> thread.start()
  42.   >>> thread.join()
  43.   >>> log
  44.   [[], 11]
  45. 
  46. we get different data.  Furthermore, changes made in the other thread
  47. don't affect data seen in this thread:
  48. 
  49.   >>> mydata.number
  50.   42
  51. 
  52. Of course, values you get from a local object, including a __dict__
  53. attribute, are for whatever thread was current at the time the
  54. attribute was read.  For that reason, you generally don't want to save
  55. these values across threads, as they apply only to the thread they
  56. came from.
  57. 
  58. You can create custom local objects by subclassing the local class:
  59. 
  60.   >>> class MyLocal(local):
  61.   ...     number = 2
  62.   ...     initialized = False
  63.   ...     def __init__(self, **kw):
  64.   ...         if self.initialized:
  65.   ...             raise SystemError('__init__ called too many times')
  66.   ...         self.initialized = True
  67.   ...         self.__dict__.update(kw)
  68.   ...     def squared(self):
  69.   ...         return self.number ** 2
  70. 
  71. This can be useful to support default values, methods and
  72. initialization.  Note that if you define an __init__ method, it will be
  73. called each time the local object is used in a separate thread.  This
  74. is necessary to initialize each thread's dictionary.
  75. 
  76. Now if we create a local object:
  77. 
  78.   >>> mydata = MyLocal(color='red')
  79. 
  80. Now we have a default number:
  81. 
  82.   >>> mydata.number
  83.   2
  84. 
  85. an initial color:
  86. 
  87.   >>> mydata.color
  88.   'red'
  89.   >>> del mydata.color
  90. 
  91. And a method that operates on the data:
  92. 
  93.   >>> mydata.squared()
  94.   4
  95. 
  96. As before, we can access the data in a separate thread:
  97. 
  98.   >>> log = []
  99.   >>> thread = threading.Thread(target=f)
 100.   >>> thread.start()
 101.   >>> thread.join()
 102.   >>> log
 103.   [[('color', 'red'), ('initialized', True)], 11]
 104. 
 105. without affecting this thread's data:
 106. 
 107.   >>> mydata.number
 108.   2
 109.   >>> mydata.color
 110.   Traceback (most recent call last):
 111.   ...
 112.   AttributeError: 'MyLocal' object has no attribute 'color'
 113. 
 114. Note that subclasses can define slots, but they are not thread
 115. local. They are shared across threads:
 116. 
 117.   >>> class MyLocal(local):
 118.   ...     __slots__ = 'number'
 119. 
 120.   >>> mydata = MyLocal()
 121.   >>> mydata.number = 42
 122.   >>> mydata.color = 'red'
 123. 
 124. So, the separate thread:
 125. 
 126.   >>> thread = threading.Thread(target=f)
 127.   >>> thread.start()
 128.   >>> thread.join()
 129. 
 130. affects what we see:
 131. 
 132.   >>> mydata.number
 133.   11
 134. 
 135. >>> del mydata
 136. """
 137. 
 138. # Threading import is at end
 139. 
 140. class _localbase(object):
 141.     __slots__ = '_local__key', '_local__args', '_local__lock'
 142. 
 143.     def __new__(cls, *args, **kw):
 144.         self = object.__new__(cls)
 145.         key = 'thread.local.' + str(id(self))
 146.         object.__setattr__(self, '_local__key', key)
 147.         object.__setattr__(self, '_local__args', (args, kw))
 148.         object.__setattr__(self, '_local__lock', RLock())
 149. 
 150.         if args or kw and (cls.__init__ is object.__init__):
 151.             raise TypeError("Initialization arguments are not supported")
 152. 
 153.         # We need to create the thread dict in anticipation of
 154.         # __init__ being called, to make sure we don't call it
 155.         # again ourselves.
 156.         dict = object.__getattribute__(self, '__dict__')
 157.         currentThread().__dict__[key] = dict
 158. 
 159.         return self
 160. 
 161. def _patch(self):
 162.     key = object.__getattribute__(self, '_local__key')
 163.     d = currentThread().__dict__.get(key)
 164.     if d is None:
 165.         d = {}
 166.         currentThread().__dict__[key] = d
 167.         object.__setattr__(self, '__dict__', d)
 168. 
 169.         # we have a new instance dict, so call out __init__ if we have
 170.         # one
 171.         cls = type(self)
 172.         if cls.__init__ is not object.__init__:
 173.             args, kw = object.__getattribute__(self, '_local__args')
 174.             cls.__init__(self, *args, **kw)
 175.     else:
 176.         object.__setattr__(self, '__dict__', d)
 177. 
 178. class local(_localbase):
 179. 
 180.     def __getattribute__(self, name):
 181.         lock = object.__getattribute__(self, '_local__lock')
 182.         lock.acquire()
 183.         try:
 184.             _patch(self)
 185.             return object.__getattribute__(self, name)
 186.         finally:
 187.             lock.release()
 188. 
 189.     def __setattr__(self, name, value):
 190.         lock = object.__getattribute__(self, '_local__lock')
 191.         lock.acquire()
 192.         try:
 193.             _patch(self)
 194.             return object.__setattr__(self, name, value)
 195.         finally:
 196.             lock.release()
 197. 
 198.     def __delattr__(self, name):
 199.         lock = object.__getattribute__(self, '_local__lock')
 200.         lock.acquire()
 201.         try:
 202.             _patch(self)
 203.             return object.__delattr__(self, name)
 204.         finally:
 205.             lock.release()
 206. 
 207. 
 208.     def __del__():
 209.         threading_enumerate = enumerate
 210.         __getattribute__ = object.__getattribute__
 211. 
 212.         def __del__(self):
 213.             key = __getattribute__(self, '_local__key')
 214. 
 215.             try:
 216.                 threads = list(threading_enumerate())
 217.             except:
 218.                 # if enumerate fails, as it seems to do during
 219.                 # shutdown, we'll skip cleanup under the assumption
 220.                 # that there is nothing to clean up
 221.                 return
 222. 
 223.             for thread in threads:
 224.                 try:
 225.                     __dict__ = thread.__dict__
 226.                 except AttributeError:
 227.                     # Thread is dying, rest in peace
 228.                     continue
 229. 
 230.                 if key in __dict__:
 231.                     try:
 232.                         del __dict__[key]
 233.                     except KeyError:
 234.                         pass # didn't have anything in this thread
 235. 
 236.         return __del__
 237.     __del__ = __del__()
 238. 
 239. from threading import currentThread, enumerate, RLock