Home | History | Annotate | Download | only in python2.7
      1 """Thread-local objects.
      2 
      3 (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`.)
      7 
      8 Thread-local objects support the management of thread-local data.
      9 If you have data that you want to be local to a thread, simply create
     10 a thread-local object and use its attributes:
     11 
     12   >>> mydata = local()
     13   >>> mydata.number = 42
     14   >>> mydata.number
     15   42
     16 
     17 You 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 
     26 What's important about thread-local objects is that their data are
     27 local 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 
     44 we get different data.  Furthermore, changes made in the other thread
     45 don't affect data seen in this thread:
     46 
     47   >>> mydata.number
     48   42
     49 
     50 Of course, values you get from a local object, including a __dict__
     51 attribute, are for whatever thread was current at the time the
     52 attribute was read.  For that reason, you generally don't want to save
     53 these values across threads, as they apply only to the thread they
     54 came from.
     55 
     56 You can create custom local objects by subclassing the local class:
     57 
     58   >>> class MyLocal(local):
     59   ...     number = 2
     60   ...     initialized = False
     61   ...     def __init__(self, **kw):
     62   ...         if self.initialized:
     63   ...             raise SystemError('__init__ called too many times')
     64   ...         self.initialized = True
     65   ...         self.__dict__.update(kw)
     66   ...     def squared(self):
     67   ...         return self.number ** 2
     68 
     69 This can be useful to support default values, methods and
     70 initialization.  Note that if you define an __init__ method, it will be
     71 called each time the local object is used in a separate thread.  This
     72 is necessary to initialize each thread's dictionary.
     73 
     74 Now if we create a local object:
     75 
     76   >>> mydata = MyLocal(color='red')
     77 
     78 Now we have a default number:
     79 
     80   >>> mydata.number
     81   2
     82 
     83 an initial color:
     84 
     85   >>> mydata.color
     86   'red'
     87   >>> del mydata.color
     88 
     89 And a method that operates on the data:
     90 
     91   >>> mydata.squared()
     92   4
     93 
     94 As before, we can access the data in a separate thread:
     95 
     96   >>> log = []
     97   >>> thread = threading.Thread(target=f)
     98   >>> thread.start()
     99   >>> thread.join()
    100   >>> log
    101   [[('color', 'red'), ('initialized', True)], 11]
    102 
    103 without affecting this thread's data:
    104 
    105   >>> mydata.number
    106   2
    107   >>> mydata.color
    108   Traceback (most recent call last):
    109   ...
    110   AttributeError: 'MyLocal' object has no attribute 'color'
    111 
    112 Note that subclasses can define slots, but they are not thread
    113 local. They are shared across threads:
    114 
    115   >>> class MyLocal(local):
    116   ...     __slots__ = 'number'
    117 
    118   >>> mydata = MyLocal()
    119   >>> mydata.number = 42
    120   >>> mydata.color = 'red'
    121 
    122 So, the separate thread:
    123 
    124   >>> thread = threading.Thread(target=f)
    125   >>> thread.start()
    126   >>> thread.join()
    127 
    128 affects what we see:
    129 
    130   >>> mydata.number
    131   11
    132 
    133 >>> del mydata
    134 """
    135 
    136 __all__ = ["local"]
    137 
    138 # We need to use objects from the threading module, but the threading
    139 # module may also want to use our `local` class, if support for locals
    140 # isn't compiled in to the `thread` module.  This creates potential problems
    141 # with circular imports.  For that reason, we don't import `threading`
    142 # until the bottom of this file (a hack sufficient to worm around the
    143 # potential problems).  Note that almost all platforms do have support for
    144 # locals in the `thread` module, and there is no circular import problem
    145 # then, so problems introduced by fiddling the order of imports here won't
    146 # manifest on most boxes.
    147 
    148 class _localbase(object):
    149     __slots__ = '_local__key', '_local__args', '_local__lock'
    150 
    151     def __new__(cls, *args, **kw):
    152         self = object.__new__(cls)
    153         key = '_local__key', 'thread.local.' + str(id(self))
    154         object.__setattr__(self, '_local__key', key)
    155         object.__setattr__(self, '_local__args', (args, kw))
    156         object.__setattr__(self, '_local__lock', RLock())
    157 
    158         if (args or kw) and (cls.__init__ is object.__init__):
    159             raise TypeError("Initialization arguments are not supported")
    160 
    161         # We need to create the thread dict in anticipation of
    162         # __init__ being called, to make sure we don't call it
    163         # again ourselves.
    164         dict = object.__getattribute__(self, '__dict__')
    165         current_thread().__dict__[key] = dict
    166 
    167         return self
    168 
    169 def _patch(self):
    170     key = object.__getattribute__(self, '_local__key')
    171     d = current_thread().__dict__.get(key)
    172     if d is None:
    173         d = {}
    174         current_thread().__dict__[key] = d
    175         object.__setattr__(self, '__dict__', d)
    176 
    177         # we have a new instance dict, so call out __init__ if we have
    178         # one
    179         cls = type(self)
    180         if cls.__init__ is not object.__init__:
    181             args, kw = object.__getattribute__(self, '_local__args')
    182             cls.__init__(self, *args, **kw)
    183     else:
    184         object.__setattr__(self, '__dict__', d)
    185 
    186 class local(_localbase):
    187 
    188     def __getattribute__(self, name):
    189         lock = object.__getattribute__(self, '_local__lock')
    190         lock.acquire()
    191         try:
    192             _patch(self)
    193             return object.__getattribute__(self, name)
    194         finally:
    195             lock.release()
    196 
    197     def __setattr__(self, name, value):
    198         if name == '__dict__':
    199             raise AttributeError(
    200                 "%r object attribute '__dict__' is read-only"
    201                 % self.__class__.__name__)
    202         lock = object.__getattribute__(self, '_local__lock')
    203         lock.acquire()
    204         try:
    205             _patch(self)
    206             return object.__setattr__(self, name, value)
    207         finally:
    208             lock.release()
    209 
    210     def __delattr__(self, name):
    211         if name == '__dict__':
    212             raise AttributeError(
    213                 "%r object attribute '__dict__' is read-only"
    214                 % self.__class__.__name__)
    215         lock = object.__getattribute__(self, '_local__lock')
    216         lock.acquire()
    217         try:
    218             _patch(self)
    219             return object.__delattr__(self, name)
    220         finally:
    221             lock.release()
    222 
    223     def __del__(self):
    224         import threading
    225 
    226         key = object.__getattribute__(self, '_local__key')
    227 
    228         try:
    229             # We use the non-locking API since we might already hold the lock
    230             # (__del__ can be called at any point by the cyclic GC).
    231             threads = threading._enumerate()
    232         except:
    233             # If enumerating the current threads fails, as it seems to do
    234             # during shutdown, we'll skip cleanup under the assumption
    235             # that there is nothing to clean up.
    236             return
    237 
    238         for thread in threads:
    239             try:
    240                 __dict__ = thread.__dict__
    241             except AttributeError:
    242                 # Thread is dying, rest in peace.
    243                 continue
    244 
    245             if key in __dict__:
    246                 try:
    247                     del __dict__[key]
    248                 except KeyError:
    249                     pass # didn't have anything in this thread
    250 
    251 from threading import current_thread, RLock
    252