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