PyXR

c:\python24\lib \ _threading_local.py



0001 """Thread-local objects
0002 
0003 (Note that this module provides a Python version of thread
0004  threading.local class.  Depending on the version of Python you're
0005  using, there may be a faster one available.  You should always import
0006  the local class from threading.)
0007 
0008 Thread-local objects support the management of thread-local data.
0009 If you have data that you want to be local to a thread, simply create
0010 a thread-local object and use its attributes:
0011 
0012   >>> mydata = local()
0013   >>> mydata.number = 42
0014   >>> mydata.number
0015   42
0016 
0017 You can also access the local-object's dictionary:
0018 
0019   >>> mydata.__dict__
0020   {'number': 42}
0021   >>> mydata.__dict__.setdefault('widgets', [])
0022   []
0023   >>> mydata.widgets
0024   []
0025 
0026 What's important about thread-local objects is that their data are
0027 local to a thread. If we access the data in a different thread:
0028 
0029   >>> log = []
0030   >>> def f():
0031   ...     items = mydata.__dict__.items()
0032   ...     items.sort()
0033   ...     log.append(items)
0034   ...     mydata.number = 11
0035   ...     log.append(mydata.number)
0036 
0037   >>> import threading
0038   >>> thread = threading.Thread(target=f)
0039   >>> thread.start()
0040   >>> thread.join()
0041   >>> log
0042   [[], 11]
0043 
0044 we get different data.  Furthermore, changes made in the other thread
0045 don't affect data seen in this thread:
0046 
0047   >>> mydata.number
0048   42
0049 
0050 Of course, values you get from a local object, including a __dict__
0051 attribute, are for whatever thread was current at the time the
0052 attribute was read.  For that reason, you generally don't want to save
0053 these values across threads, as they apply only to the thread they
0054 came from.
0055 
0056 You can create custom local objects by subclassing the local class:
0057 
0058   >>> class MyLocal(local):
0059   ...     number = 2
0060   ...     initialized = False
0061   ...     def __init__(self, **kw):
0062   ...         if self.initialized:
0063   ...             raise SystemError('__init__ called too many times')
0064   ...         self.initialized = True
0065   ...         self.__dict__.update(kw)
0066   ...     def squared(self):
0067   ...         return self.number ** 2
0068 
0069 This can be useful to support default values, methods and
0070 initialization.  Note that if you define an __init__ method, it will be
0071 called each time the local object is used in a separate thread.  This
0072 is necessary to initialize each thread's dictionary.
0073 
0074 Now if we create a local object:
0075 
0076   >>> mydata = MyLocal(color='red')
0077 
0078 Now we have a default number:
0079 
0080   >>> mydata.number
0081   2
0082 
0083 an initial color:
0084 
0085   >>> mydata.color
0086   'red'
0087   >>> del mydata.color
0088 
0089 And a method that operates on the data:
0090 
0091   >>> mydata.squared()
0092   4
0093 
0094 As before, we can access the data in a separate thread:
0095 
0096   >>> log = []
0097   >>> thread = threading.Thread(target=f)
0098   >>> thread.start()
0099   >>> thread.join()
0100   >>> log
0101   [[('color', 'red'), ('initialized', True)], 11]
0102 
0103 without affecting this thread's data:
0104 
0105   >>> mydata.number
0106   2
0107   >>> mydata.color
0108   Traceback (most recent call last):
0109   ...
0110   AttributeError: 'MyLocal' object has no attribute 'color'
0111 
0112 Note that subclasses can define slots, but they are not thread
0113 local. They are shared across threads:
0114 
0115   >>> class MyLocal(local):
0116   ...     __slots__ = 'number'
0117 
0118   >>> mydata = MyLocal()
0119   >>> mydata.number = 42
0120   >>> mydata.color = 'red'
0121 
0122 So, the separate thread:
0123 
0124   >>> thread = threading.Thread(target=f)
0125   >>> thread.start()
0126   >>> thread.join()
0127 
0128 affects what we see:
0129 
0130   >>> mydata.number
0131   11
0132 
0133 >>> del mydata
0134 """
0135 
0136 # Threading import is at end
0137 
0138 class _localbase(object):
0139     __slots__ = '_local__key', '_local__args', '_local__lock'
0140 
0141     def __new__(cls, *args, **kw):
0142         self = object.__new__(cls)
0143         key = '_local__key', 'thread.local.' + str(id(self))
0144         object.__setattr__(self, '_local__key', key)
0145         object.__setattr__(self, '_local__args', (args, kw))
0146         object.__setattr__(self, '_local__lock', RLock())
0147 
0148         if args or kw and (cls.__init__ is object.__init__):
0149             raise TypeError("Initialization arguments are not supported")
0150 
0151         # We need to create the thread dict in anticipation of
0152         # __init__ being called, to make sire we don't cal it
0153         # again ourselves.
0154         dict = object.__getattribute__(self, '__dict__')
0155         currentThread().__dict__[key] = dict
0156 
0157         return self
0158 
0159 def _patch(self):
0160     key = object.__getattribute__(self, '_local__key')
0161     d = currentThread().__dict__.get(key)
0162     if d is None:
0163         d = {}
0164         currentThread().__dict__[key] = d
0165         object.__setattr__(self, '__dict__', d)
0166 
0167         # we have a new instance dict, so call out __init__ if we have
0168         # one
0169         cls = type(self)
0170         if cls.__init__ is not object.__init__:
0171             args, kw = object.__getattribute__(self, '_local__args')
0172             cls.__init__(self, *args, **kw)
0173     else:
0174         object.__setattr__(self, '__dict__', d)
0175 
0176 class local(_localbase):
0177 
0178     def __getattribute__(self, name):
0179         lock = object.__getattribute__(self, '_local__lock')
0180         lock.acquire()
0181         try:
0182             _patch(self)
0183             return object.__getattribute__(self, name)
0184         finally:
0185             lock.release()
0186 
0187     def __setattr__(self, name, value):
0188         lock = object.__getattribute__(self, '_local__lock')
0189         lock.acquire()
0190         try:
0191             _patch(self)
0192             return object.__setattr__(self, name, value)
0193         finally:
0194             lock.release()
0195 
0196     def __delattr__(self, name):
0197         lock = object.__getattribute__(self, '_local__lock')
0198         lock.acquire()
0199         try:
0200             _patch(self)
0201             return object.__delattr__(self, name)
0202         finally:
0203             lock.release()
0204 
0205 
0206     def __del__():
0207         threading_enumerate = enumerate
0208         __getattribute__ = object.__getattribute__
0209 
0210         def __del__(self):
0211             key = __getattribute__(self, '_local__key')
0212 
0213             try:
0214                 threads = list(threading_enumerate())
0215             except:
0216                 # if enumerate fails, as it seems to do during
0217                 # shutdown, we'll skip cleanup under the assumption
0218                 # that there is nothing to clean up
0219                 return
0220 
0221             for thread in threads:
0222                 try:
0223                     __dict__ = thread.__dict__
0224                 except AttributeError:
0225                     # Thread is dying, rest in peace
0226                     continue
0227 
0228                 if key in __dict__:
0229                     try:
0230                         del __dict__[key]
0231                     except KeyError:
0232                         pass # didn't have anything in this thread
0233 
0234         return __del__
0235     __del__ = __del__()
0236 
0237 from threading import currentThread, enumerate, RLock
0238 

Generated by PyXR 0.9.4
SourceForge.net Logo